diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..09d6e96 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,231 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..5fc5dcd --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,29 @@ +name: Deploy to GitHub Releases + +on: + push: + tags: + - '**' # This pattern matches tags starting with 'v' + +jobs: + deploy-to-github-releases: + runs-on: windows-latest + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Get Version from Tag + id: get-version + shell: bash + run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT + - name: Install .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.0.x + - name: Publish Application + run: dotnet publish MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj -c Release -o publish -r win-x64 + - name: Create Velopack Release + run: | + dotnet tool install -g vpk + vpk download github --repoUrl https://github.com/the-database/MangaJaNaiConverterGui + vpk pack -u MangaJaNaiConverterGui -v ${{ steps.get-version.outputs.version }} -p publish + vpk upload github --repoUrl https://github.com/the-database/MangaJaNaiConverterGui --publish --releaseName "${{ steps.get-version.outputs.version }}" --tag ${{ steps.get-version.outputs.version }} -i ./MangaJaNaiConverterGui/assets/logo.ico -e MangaJaNaiConverterGui.exe diff --git a/MangaJaNaiConverterGui.sln b/MangaJaNaiConverterGui.sln index 356022f..df788f9 100644 --- a/MangaJaNaiConverterGui.sln +++ b/MangaJaNaiConverterGui.sln @@ -5,7 +5,10 @@ VisualStudioVersion = 17.7.34031.279 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MangaJaNaiConverterGui", "MangaJaNaiConverterGui\MangaJaNaiConverterGui.csproj", "{A0F428F0-2B21-415A-B6F0-23D52FB3300E}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SevenZipExtractor", "SevenZipExtractor\SevenZipExtractor.csproj", "{E45E8242-84F3-4F83-8746-493B488F72BF}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9FA41E85-6A01-4BB3-90FB-36C1A91C1D52}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -17,10 +20,6 @@ Global {A0F428F0-2B21-415A-B6F0-23D52FB3300E}.Debug|Any CPU.Build.0 = Debug|Any CPU {A0F428F0-2B21-415A-B6F0-23D52FB3300E}.Release|Any CPU.ActiveCfg = Release|Any CPU {A0F428F0-2B21-415A-B6F0-23D52FB3300E}.Release|Any CPU.Build.0 = Release|Any CPU - {E45E8242-84F3-4F83-8746-493B488F72BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E45E8242-84F3-4F83-8746-493B488F72BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E45E8242-84F3-4F83-8746-493B488F72BF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E45E8242-84F3-4F83-8746-493B488F72BF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/MangaJaNaiConverterGui/App.axaml.cs b/MangaJaNaiConverterGui/App.axaml.cs index 5c0dfdc..9b3cbd5 100644 --- a/MangaJaNaiConverterGui/App.axaml.cs +++ b/MangaJaNaiConverterGui/App.axaml.cs @@ -1,14 +1,13 @@ +using Autofac; using Avalonia; using Avalonia.Markup.Xaml; using Avalonia.ReactiveUI; +using MangaJaNaiConverterGui.Services; using MangaJaNaiConverterGui.ViewModels; using MangaJaNaiConverterGui.Views; using ReactiveUI; -using System; +using Splat.Autofac; using System.IO; -using System.Reactive.Linq; -using System.Reactive; -using System.Reflection; namespace MangaJaNaiConverterGui { @@ -31,10 +30,33 @@ public override void OnFrameworkInitializationCompleted() File.Copy(Program.AppStateFilename, Program.AppStatePath); } + // Create a new Autofac container builder. + var builder = new ContainerBuilder(); + builder.RegisterType().AsSelf(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + // etc. + + // Register the Adapter to Splat. + // Creates and sets the Autofac resolver as the Locator. + var autofacResolver = builder.UseAutofacDependencyResolver(); + + // Register the resolver in Autofac so it can be later resolved. + builder.RegisterInstance(autofacResolver); + + // Initialize ReactiveUI components. + autofacResolver.InitializeReactiveUI(); + + var container = builder.Build(); + + autofacResolver.SetLifetimeScope(container); + + //var vm = container.Resolve(); + var suspension = new AutoSuspendHelper(ApplicationLifetime); RxApp.SuspensionHost.CreateNewAppState = () => new MainWindowViewModel(); RxApp.SuspensionHost.SetupDefaultSuspendResume(Program.SuspensionDriver); - suspension.OnFrameworkInitializationCompleted(); + suspension.OnFrameworkInitializationCompleted(); // Load the saved view model state. var state = RxApp.SuspensionHost.GetAppState(); diff --git a/MangaJaNaiConverterGui/NewtonsoftJsonSuspensionDriver.cs b/MangaJaNaiConverterGui/Drivers/NewtonsoftJsonSuspensionDriver.cs similarity index 86% rename from MangaJaNaiConverterGui/NewtonsoftJsonSuspensionDriver.cs rename to MangaJaNaiConverterGui/Drivers/NewtonsoftJsonSuspensionDriver.cs index 944c800..350cf89 100644 --- a/MangaJaNaiConverterGui/NewtonsoftJsonSuspensionDriver.cs +++ b/MangaJaNaiConverterGui/Drivers/NewtonsoftJsonSuspensionDriver.cs @@ -1,14 +1,11 @@ -using MangaJaNaiConverterGui.ViewModels; -using Avalonia.Controls; -using Avalonia.Controls.Templates; +using Newtonsoft.Json; using ReactiveUI; using System; -using System.Reactive.Linq; -using System.Reactive; -using Newtonsoft.Json; using System.IO; +using System.Reactive; +using System.Reactive.Linq; -namespace MangaJaNaiConverterGui +namespace MangaJaNaiConverterGui.Drivers { public class NewtonsoftJsonSuspensionDriver : ISuspensionDriver { @@ -31,7 +28,7 @@ public IObservable InvalidateState() public IObservable LoadState() { var lines = File.ReadAllText(_file); - var state = JsonConvert.DeserializeObject(lines, Settings); + var state = JsonConvert.DeserializeObject(lines, Settings)!; return Observable.Return(state); } diff --git a/MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj b/MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj index 5bbb2d2..3371d20 100644 --- a/MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj +++ b/MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj @@ -1,5 +1,12 @@  + WinExe net8.0 enable @@ -9,6 +16,7 @@ 1.0.0 logo.png Assets\logo.ico + true @@ -23,23 +31,20 @@ - - - - + + + + - - - + + + - + - - - - - - + + + @@ -47,7 +52,7 @@ Always - + PreserveNewest diff --git a/MangaJaNaiConverterGui/Program.cs b/MangaJaNaiConverterGui/Program.cs index e674039..11335c6 100644 --- a/MangaJaNaiConverterGui/Program.cs +++ b/MangaJaNaiConverterGui/Program.cs @@ -1,16 +1,17 @@ using Avalonia; using Avalonia.ReactiveUI; -using NuGet.Versioning; -using Velopack; +using MangaJaNaiConverterGui.Drivers; +using ReactiveUI; using System; -using Microsoft.Extensions.Logging; using System.IO; -using ReactiveUI; +using Velopack; namespace MangaJaNaiConverterGui { internal class Program { + public static bool WasFirstRun { get; private set; } + public static readonly string AppStateFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MangaJaNaiConverterGui" @@ -26,7 +27,26 @@ internal class Program [STAThread] public static void Main(string[] args) { - VelopackApp.Build().Run(); + VelopackApp.Build() + .WithBeforeUninstallFastCallback((v) => + { + // On uninstall, remove Python and models from app data + var pythonDir = Path.Combine(AppStateFolder, "python"); + var modelsDir = Path.Combine(AppStateFolder, "models"); + if (Directory.Exists(pythonDir)) + { + Directory.Delete(pythonDir, true); + } + if (Directory.Exists(modelsDir)) + { + Directory.Delete(modelsDir, true); + } + }) + .WithFirstRun(_ => + { + WasFirstRun = true; + }) + .Run(); BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } diff --git a/MangaJaNaiConverterGui/Services/Downloader.cs b/MangaJaNaiConverterGui/Services/Downloader.cs new file mode 100644 index 0000000..e61a79b --- /dev/null +++ b/MangaJaNaiConverterGui/Services/Downloader.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; + + +namespace MangaJaNaiConverterGui.Services +{ + public class Downloader + { + public delegate void ProgressChanged(double percentage); + + public static async Task DownloadFileAsync(string url, string destinationFilePath, ProgressChanged progressChanged) + { + using HttpClient client = new(); + using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + + response.EnsureSuccessStatusCode(); + + long totalBytes = response.Content.Headers.ContentLength ?? -1L; + using Stream contentStream = await response.Content.ReadAsStreamAsync(), fileStream = new FileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true); + + var totalRead = 0L; + var buffer = new byte[8192]; + int read; + + while ((read = await contentStream.ReadAsync(buffer)) > 0) + { + await fileStream.WriteAsync(buffer.AsMemory(0, read)); + totalRead += read; + + if (totalBytes != -1) + { + double percentage = Math.Round((double)totalRead / totalBytes * 100, 0); + progressChanged?.Invoke(percentage); + } + } + } + } +} diff --git a/MangaJaNaiConverterGui/ETACalculator.cs b/MangaJaNaiConverterGui/Services/ETACalculator.cs similarity index 82% rename from MangaJaNaiConverterGui/ETACalculator.cs rename to MangaJaNaiConverterGui/Services/ETACalculator.cs index 53b3780..35cb39b 100644 --- a/MangaJaNaiConverterGui/ETACalculator.cs +++ b/MangaJaNaiConverterGui/Services/ETACalculator.cs @@ -1,12 +1,10 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Text; using ProgressItem = System.Collections.Generic.KeyValuePair; -namespace Progression.Extras +namespace MangaJaNaiConverterGui.Services { public interface IETACalculator { @@ -51,9 +49,9 @@ public class ETACalculator : IETACalculator public ETACalculator(int minimumData, double maximumDuration) { this.minimumData = minimumData; - this.maximumTicks = (long)(maximumDuration * Stopwatch.Frequency); - this.queue = new Queue(minimumData * 2); - this.timer = Stopwatch.StartNew(); + maximumTicks = (long)(maximumDuration * Stopwatch.Frequency); + queue = new Queue(minimumData * 2); + timer = Stopwatch.StartNew(); } private int minimumData; @@ -74,10 +72,10 @@ public void Reset() private void ClearExpired() { - var expired = timer.ElapsedTicks - this.maximumTicks; - while (queue.Count > this.minimumData && queue.Peek().Key < expired) + var expired = timer.ElapsedTicks - maximumTicks; + while (queue.Count > minimumData && queue.Peek().Key < expired) { - this.oldest = queue.Dequeue(); + oldest = queue.Dequeue(); } } @@ -88,7 +86,7 @@ private void ClearExpired() public void Update(float progress) { // If progress hasn't changed, ignore: - if (this.current.Value == progress) + if (current.Value == progress) { return; } @@ -98,13 +96,13 @@ public void Update(float progress) // Queue this item: long currentTicks = timer.ElapsedTicks; - this.current = new ProgressItem(currentTicks, progress); - this.queue.Enqueue(this.current); + current = new ProgressItem(currentTicks, progress); + queue.Enqueue(current); // See if its the first item: - if (this.queue.Count == 1) + if (queue.Count == 1) { - this.oldest = this.current; + oldest = current; } } @@ -120,7 +118,7 @@ public TimeSpan ETR var current = this.current; // Make sure we have enough items: - if (queue.Count < this.minimumData || oldest.Value == current.Value) + if (queue.Count < minimumData || oldest.Value == current.Value) { return TimeSpan.MaxValue; } @@ -150,7 +148,7 @@ public bool ETAIsAvailable get { // Make sure we have enough items: - return (queue.Count >= this.minimumData && oldest.Value != current.Value); + return queue.Count >= minimumData && oldest.Value != current.Value; } } diff --git a/MangaJaNaiConverterGui/Services/IPythonService.cs b/MangaJaNaiConverterGui/Services/IPythonService.cs new file mode 100644 index 0000000..7ad0c28 --- /dev/null +++ b/MangaJaNaiConverterGui/Services/IPythonService.cs @@ -0,0 +1,18 @@ +using Avalonia.Collections; + +namespace MangaJaNaiConverterGui.Services +{ + public interface IPythonService + { + bool IsPythonInstalled(); + bool AreModelsInstalled(); + string PythonDirectory { get; } + string ModelsDirectory { get; } + string PythonPath { get; } + string InstallUpdatePythonDependenciesCommand { get; } + void ExtractTgz(string gzArchiveName, string destFolder); + void ExtractZip(string archivePath, string outFolder, ProgressChanged progressChanged); + void AddPythonPth(string destFolder); + AvaloniaList AllModels { get; } + } +} diff --git a/MangaJaNaiConverterGui/Services/IUpdateManagerService.cs b/MangaJaNaiConverterGui/Services/IUpdateManagerService.cs new file mode 100644 index 0000000..dcab6c5 --- /dev/null +++ b/MangaJaNaiConverterGui/Services/IUpdateManagerService.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading.Tasks; +using Velopack; + +namespace MangaJaNaiConverterGui.Services +{ + public interface IUpdateManagerService + { + bool IsInstalled { get; } + string AppVersion { get; } + bool IsUpdatePendingRestart { get; } + void ApplyUpdatesAndRestart(UpdateInfo update); + Task CheckForUpdatesAsync(); + Task DownloadUpdatesAsync(UpdateInfo update, Action? progress = null); + } +} diff --git a/MangaJaNaiConverterGui/Services/PythonService.cs b/MangaJaNaiConverterGui/Services/PythonService.cs new file mode 100644 index 0000000..0daca6b --- /dev/null +++ b/MangaJaNaiConverterGui/Services/PythonService.cs @@ -0,0 +1,187 @@ +using Avalonia.Collections; +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.Tar; +using ICSharpCode.SharpZipLib.Zip; +using Splat; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; + +namespace MangaJaNaiConverterGui.Services +{ + public delegate void ProgressChanged(double percentage); + + // https://github.com/chaiNNer-org/chaiNNer/blob/main/src/main/python/integratedPython.ts + public class PythonService : IPythonService + { + private readonly IUpdateManagerService _updateManagerService; + + public static readonly Dictionary PYTHON_DOWNLOADS = new() + { + { + "win32", + new PythonDownload + { + Url = "https://github.com/indygreg/python-build-standalone/releases/download/20240415/cpython-3.11.9+20240415-x86_64-pc-windows-msvc-shared-install_only.tar.gz", + Path = "python/python.exe", + Version = "3.11.9", + Filename = "Python.tar.gz" + } + }, + }; + + public PythonService(IUpdateManagerService? updateManagerService = null) + { + _updateManagerService = updateManagerService ?? Locator.Current.GetService()!; + } + + public string ModelsDirectory => (_updateManagerService?.IsInstalled ?? false) ? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"MangaJaNaiConverterGui\models") : Path.GetFullPath(@".\backend\models"); + + public string PythonDirectory => (_updateManagerService?.IsInstalled ?? false) ? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"MangaJaNaiConverterGui\python") : Path.GetFullPath(@".\backend\python"); + public string PythonPath => Path.GetFullPath(Path.Join(PythonDirectory, PYTHON_DOWNLOADS["win32"].Path)); + + public bool IsPythonInstalled() => File.Exists(PythonPath); + public bool AreModelsInstalled() => Directory.Exists(ModelsDirectory) && Directory.GetFiles(ModelsDirectory).Length > 0; + + public class PythonDownload + { + public string Url { get; set; } + public string Version { get; set; } + public string Path { get; set; } + public string Filename { get; set; } + } + + public void ExtractTgz(string gzArchiveName, string destFolder) + { + Stream inStream = File.OpenRead(gzArchiveName); + Stream gzipStream = new GZipInputStream(inStream); + + TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream, Encoding.UTF8); + tarArchive.ExtractContents(destFolder); + tarArchive.Close(); + + gzipStream.Close(); + inStream.Close(); + } + + public void ExtractZip(string archivePath, string outFolder, ProgressChanged progressChanged) + { + + using (var fsInput = File.OpenRead(archivePath)) + using (var zf = new ZipFile(fsInput)) + { + + for (var i = 0; i < zf.Count; i++) + { + ZipEntry zipEntry = zf[i]; + + if (!zipEntry.IsFile) + { + // Ignore directories + continue; + } + String entryFileName = zipEntry.Name; + // to remove the folder from the entry: + //entryFileName = Path.GetFileName(entryFileName); + // Optionally match entrynames against a selection list here + // to skip as desired. + // The unpacked length is available in the zipEntry.Size property. + + // Manipulate the output filename here as desired. + var fullZipToPath = Path.Combine(outFolder, entryFileName); + var directoryName = Path.GetDirectoryName(fullZipToPath); + if (directoryName.Length > 0) + { + Directory.CreateDirectory(directoryName); + } + + // 4K is optimum + var buffer = new byte[4096]; + + // Unzip file in buffered chunks. This is just as fast as unpacking + // to a buffer the full size of the file, but does not waste memory. + // The "using" will close the stream even if an exception occurs. + using (var zipStream = zf.GetInputStream(zipEntry)) + using (Stream fsOutput = File.Create(fullZipToPath)) + { + StreamUtils.Copy(zipStream, fsOutput, buffer); + } + + var percentage = Math.Round((double)i / zf.Count * 100, 0); + progressChanged?.Invoke(percentage); + } + } + } + + public void AddPythonPth(string destFolder) + { + string[] lines = { "python311.zip", "DLLs", "Lib", ".", "Lib/site-packages" }; + var filename = "python311._pth"; + + using var outputFile = new StreamWriter(Path.Combine(destFolder, filename)); + + foreach (string line in lines) + outputFile.WriteLine(line); + } + + public string InstallUpdatePythonDependenciesCommand + { + get + { + string[] dependencies = { + "spandrel^>=0.3.4", + "spandrel_extra_arches^>=0.1.1", + "opencv-python^>=4.10.0.84", + "pillow-avif-plugin^>=1.4.6", + "rarfile^>=4.2", + "multiprocess^>=0.70.16", + "chainner_ext^>=0.3.10", + "sanic^>=24.6.0", + "pynvml^>=11.5.3", + "psutil^>=6.0.0" + }; + + return $@"{PythonPath} -m pip install torch^>=2.3.1 torchvision^>=0.18.1 --index-url https://download.pytorch.org/whl/cu121 && {PythonPath} -m pip install {string.Join(" ", dependencies)}"; + } + } + + private AvaloniaList? _allModels; + + public AvaloniaList AllModels + { + get + { + if (_allModels == null) + { + + try + { + var models = new AvaloniaList(Directory.GetFiles(ModelsDirectory).Where(filename => + Path.GetExtension(filename).Equals(".pth", StringComparison.CurrentCultureIgnoreCase) || + Path.GetExtension(filename).Equals(".pt", StringComparison.CurrentCultureIgnoreCase) || + Path.GetExtension(filename).Equals(".ckpt", StringComparison.CurrentCultureIgnoreCase) || + Path.GetExtension(filename).Equals(".safetensors", StringComparison.CurrentCultureIgnoreCase) + ) + .Select(filename => Path.GetFileName(filename)) + .Order().ToList()); + + Debug.WriteLine($"GetAllModels: {models.Count}"); + + _allModels = models; + } + catch (DirectoryNotFoundException) + { + Debug.WriteLine($"GetAllModels: DirectoryNotFoundException"); + return []; + } + } + + return _allModels; + } + } + } +} diff --git a/MangaJaNaiConverterGui/Services/UpdateManagerService.cs b/MangaJaNaiConverterGui/Services/UpdateManagerService.cs new file mode 100644 index 0000000..da449e4 --- /dev/null +++ b/MangaJaNaiConverterGui/Services/UpdateManagerService.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Velopack; +using Velopack.Sources; + +namespace MangaJaNaiConverterGui.Services +{ + public class UpdateManagerService : IUpdateManagerService + { + private readonly UpdateManager _um; + + public UpdateManagerService() + { + _um = new UpdateManager(new GithubSource("https://github.com/the-database/MangaJaNaiConverterGui", null, false)); + } + + public string AppVersion { get => _um?.CurrentVersion?.ToString() ?? ""; } + + public bool IsInstalled { get => _um.IsInstalled; } + + public bool IsUpdatePendingRestart { get => _um.IsUpdatePendingRestart; } + + public void ApplyUpdatesAndRestart(UpdateInfo update) + { + _um.ApplyUpdatesAndRestart(update); + } + + public async Task CheckForUpdatesAsync() + { + return await _um.CheckForUpdatesAsync(); + } + + public Task DownloadUpdatesAsync(UpdateInfo update, Action? progress = null) + { + return _um.DownloadUpdatesAsync(update, progress); + } + } +} diff --git a/MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs b/MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs index a4b3238..2be0c9c 100644 --- a/MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs +++ b/MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs @@ -1,9 +1,10 @@ using Avalonia.Collections; using Avalonia.Threading; +using MangaJaNaiConverterGui.Drivers; +using MangaJaNaiConverterGui.Services; using Newtonsoft.Json; -using Progression.Extras; using ReactiveUI; -using SevenZipExtractor; +using Splat; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -17,7 +18,6 @@ using System.Threading; using System.Threading.Tasks; using Velopack; -using Velopack.Sources; using File = System.IO.File; namespace MangaJaNaiConverterGui.ViewModels @@ -31,11 +31,16 @@ public class MainWindowViewModel : ViewModelBase private readonly DispatcherTimer _timer = new(); private static readonly HttpClient client = new(); - private readonly UpdateManager _um; private UpdateInfo? _update = null; - public MainWindowViewModel() + private readonly IPythonService _pythonService; + private readonly IUpdateManagerService _updateManagerService; + + public MainWindowViewModel(IPythonService? pythonService = null, IUpdateManagerService? updateManagerService = null) { + _pythonService = pythonService ?? Locator.Current.GetService()!; + _updateManagerService = updateManagerService ?? Locator.Current.GetService()!; + var g1 = this.WhenAnyValue ( x => x.SelectedWorkflowIndex @@ -49,8 +54,6 @@ public MainWindowViewModel() ShowDialog = new Interaction(); - _um = new UpdateManager(new GithubSource("https://github.com/the-database/MangaJaNaiConverterGui", null, false)); - CheckAndDoBackup(); CheckForUpdates(); } @@ -95,7 +98,9 @@ private void _timer_Tick(object? sender, EventArgs e) public string TotalEta => _totalEtaCalculator.ETAIsAvailable ? _totalEtaCalculator.ETA.ToString("t") : _archiveEtaCalculator.ETAIsAvailable ? DateTime.Now.Add(TotalEtr).ToString("t") : "please wait"; - public bool IsInstalled => _um?.IsInstalled ?? false; + public bool IsInstalled => _updateManagerService.IsInstalled; + [DataMember] + public string ModelsDirectory => _pythonService.ModelsDirectory; private bool _showCheckUpdateButton = true; public bool ShowCheckUpdateButton @@ -126,7 +131,7 @@ public bool ShowApplyButton } } - public string AppVersion => _um?.CurrentVersion?.ToString() ?? ""; + public string AppVersion => _updateManagerService.AppVersion; private string _updateStatusText = string.Empty; public string UpdateStatusText @@ -232,6 +237,31 @@ public string ValidationText } } + private string _backendSetupMainStatus = string.Empty; + public string BackendSetupMainStatus + { + get => this._backendSetupMainStatus; + set + { + this.RaiseAndSetIfChanged(ref _backendSetupMainStatus, value); + } + } + + public string BackendSetupSubStatusText => string.Join("\n", BackendSetupSubStatusQueue); + + private static readonly int BACKEND_SETUP_SUB_STATUS_QUEUE_CAPACITY = 50; + + private ConcurrentQueue _backendSetupSubStatusQueue = new(); + public ConcurrentQueue BackendSetupSubStatusQueue + { + get => this._backendSetupSubStatusQueue; + set + { + this.RaiseAndSetIfChanged(ref _backendSetupSubStatusQueue, value); + this.RaisePropertyChanged(nameof(BackendSetupSubStatusText)); + } + } + public string ConsoleText => string.Join("\n", ConsoleQueue); private static readonly int CONSOLE_QUEUE_CAPACITY = 1000; @@ -266,6 +296,8 @@ public bool RequestShowAppSettings } } + public string PythonPath => _pythonService.PythonPath; + private bool _isExtractingBackend = true; public bool IsExtractingBackend { @@ -349,9 +381,9 @@ public TimeSpan ElapsedTime } } - private AvaloniaList _workflows; + private AvaloniaList? _workflows; [DataMember] - public AvaloniaList Workflows + public AvaloniaList? Workflows { get => _workflows; set => this.RaiseAndSetIfChanged(ref _workflows, value); @@ -364,9 +396,9 @@ public AvaloniaList Workflows public int SelectedWorkflowIndex { get => _selectedWorkflowIndex; - set - { - this.RaiseAndSetIfChanged(ref _selectedWorkflowIndex, value); + set + { + this.RaiseAndSetIfChanged(ref _selectedWorkflowIndex, value); this.RaisePropertyChanged(nameof(CurrentWorkflow)); this.RaisePropertyChanged(nameof(CurrentWorkflow.ActiveWorkflow)); } @@ -375,8 +407,8 @@ public int SelectedWorkflowIndex public UpscaleWorkflow? CurrentWorkflow { get => Workflows?[SelectedWorkflowIndex]; - set - { + set + { if (Workflows != null) { Workflows[SelectedWorkflowIndex] = value; @@ -419,7 +451,7 @@ public async Task RunUpscale() ProgressCurrentFileInArchive = 0; ShowArchiveProgressBar = false; - var cmd = $@".\python\python.exe "".\backend\src\runmangajanaiconverterguiupscale.py"" --settings {Program.AppStatePath}"; + var cmd = $@"{_pythonService.PythonPath} "".\src\run_upscale.py"" --settings {Program.AppStatePath}"; ConsoleQueueEnqueue($"Upscaling with command: {cmd}"); await RunCommand($@" /C {cmd}"); @@ -630,7 +662,8 @@ public void CheckInputs() public void AddChain() { - CurrentWorkflow?.Chains.Add(new UpscaleChain { + CurrentWorkflow?.Chains.Add(new UpscaleChain + { Vm = this, }); UpdateChainHeaders(); @@ -672,7 +705,7 @@ public async Task RunCommand(string command) process.StartInfo.RedirectStandardError = true; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; - process.StartInfo.WorkingDirectory = Path.GetFullPath(@".\chaiNNer"); + process.StartInfo.WorkingDirectory = Path.GetFullPath(@".\backend"); process.StartInfo.StandardOutputEncoding = Encoding.UTF8; process.StartInfo.StandardErrorEncoding = Encoding.UTF8; @@ -737,7 +770,7 @@ public async Task RunCommand(string command) public async Task InitializeDeviceList() { - if (!File.Exists(@".\chaiNNer\backend\src\device_list.py")) + if (!File.Exists(@".\backend\src\device_list.py")) { return []; } @@ -747,12 +780,12 @@ public async Task InitializeDeviceList() { _runningProcess = process; process.StartInfo.FileName = "cmd.exe"; - process.StartInfo.Arguments = @"/C .\python\python.exe .\backend\src\device_list.py"; + process.StartInfo.Arguments = @$"/C {_pythonService.PythonPath} .\src\device_list.py"; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; - process.StartInfo.WorkingDirectory = Path.GetFullPath(@".\chaiNNer"); + process.StartInfo.WorkingDirectory = Path.GetFullPath(@".\backend"); process.StartInfo.StandardOutputEncoding = Encoding.UTF8; process.StartInfo.StandardErrorEncoding = Encoding.UTF8; @@ -836,6 +869,16 @@ private void ConsoleQueueEnqueue(string value) this.RaisePropertyChanged(nameof(ConsoleText)); } + private void BackendSetupSubStatusQueueEnqueue(string value) + { + while (BackendSetupSubStatusQueue.Count > BACKEND_SETUP_SUB_STATUS_QUEUE_CAPACITY) + { + BackendSetupSubStatusQueue.TryDequeue(out var _); + } + BackendSetupSubStatusQueue.Enqueue(value); + this.RaisePropertyChanged(nameof(BackendSetupSubStatusText)); + } + public void ReadWorkflowFileToCurrentWorkflow(string fullPath) { if (!File.Exists(fullPath)) @@ -861,23 +904,50 @@ public void WriteCurrentWorkflowToFile(string fullPath) public async void CheckAndExtractBackend() { - await Task.Run(() => + await Task.Run(async () => { - var backendArchivePath = Path.GetFullPath("./chaiNNer.7z"); + IsExtractingBackend = true; - if (File.Exists(backendArchivePath)) + if (!_pythonService.IsPythonInstalled()) { - IsExtractingBackend = true; - using ArchiveFile archiveFile = new(backendArchivePath); - archiveFile.Extract("."); - archiveFile.Dispose(); - File.Delete(backendArchivePath); - IsExtractingBackend = false; + // Download Python tgz + BackendSetupMainStatus = "Downloading Python..."; + var download = PythonService.PYTHON_DOWNLOADS["win32"]; + var targetPath = Path.Join(_pythonService.PythonDirectory, download.Filename); + Directory.CreateDirectory(_pythonService.PythonDirectory); + await Downloader.DownloadFileAsync(download.Url, targetPath, (progress) => + { + BackendSetupMainStatus = $"Downloading Python ({progress}%)..."; + }); + + // Extract Python tgz + BackendSetupMainStatus = "Extracting Python..."; + _pythonService.ExtractTgz(targetPath, _pythonService.PythonDirectory); + File.Delete(targetPath); + + var pthDirectory = Path.GetDirectoryName(_pythonService.PythonPath) ?? throw new ArgumentNullException("pthDirectory"); + + _pythonService.AddPythonPth(pthDirectory); + + // Install Python Dependencies + BackendSetupMainStatus = "Installing Python Dependencies (this may take several minutes)..."; + await InstallUpdatePythonDependencies(); } else { - IsExtractingBackend = false; + if (Program.WasFirstRun) + { + BackendSetupMainStatus = "Checking Python Dependencies..."; + await InstallUpdatePythonDependencies(); + } + } + + if (!_pythonService.AreModelsInstalled()) + { + await DownloadModels(); } + + IsExtractingBackend = false; }); DeviceList = await InitializeDeviceList(); @@ -888,6 +958,95 @@ await Task.Run(() => } } + public async Task DownloadModels() + { + BackendSetupMainStatus = "Downloading MangaJaNai Models..."; + var download = "https://github.com/the-database/mangajanai/releases/download/1.0.0/MangaJaNai_V1_ModelsOnly.zip"; + var targetPath = Path.Join(_pythonService.ModelsDirectory, "mangajanai.zip"); + Directory.CreateDirectory(_pythonService.ModelsDirectory); + await Downloader.DownloadFileAsync(download, targetPath, (progress) => + { + BackendSetupMainStatus = $"Downloading MangaJaNai Models ({progress}%)..."; + }); + + BackendSetupMainStatus = "Extracting MangaJaNai Models..."; + _pythonService.ExtractZip(targetPath, _pythonService.ModelsDirectory, (double progress) => + { + BackendSetupMainStatus = $"Extracting MangaJaNai Models ({progress}%)..."; + }); + File.Delete(targetPath); + + BackendSetupMainStatus = "Downloading IllustrationJaNai Models..."; + download = "https://github.com/the-database/mangajanai/releases/download/1.0.0/IllustrationJaNai_V1_ModelsOnly.zip"; + targetPath = Path.Join(_pythonService.ModelsDirectory, "illustrationjanai.zip"); + await Downloader.DownloadFileAsync(download, targetPath, (progress) => + { + BackendSetupMainStatus = $"Downloading IllustrationJaNai Models ({progress}%)..."; + }); + + BackendSetupMainStatus = "Extracting IllustrationJaNai Models..."; + _pythonService.ExtractZip(targetPath, _pythonService.ModelsDirectory, (double progress) => + { + BackendSetupMainStatus = $"Extracting IllustrationJaNai Models ({progress}%)..."; + }); + File.Delete(targetPath); + } + + + + public async Task InstallUpdatePythonDependencies() + { + var cmd = _pythonService.InstallUpdatePythonDependenciesCommand; + Debug.WriteLine(cmd); + + // Create a new process to run the CMD command + using (var process = new Process()) + { + process.StartInfo.FileName = "cmd.exe"; + process.StartInfo.Arguments = @$"/C {cmd}"; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.UseShellExecute = false; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.StandardOutputEncoding = Encoding.UTF8; + process.StartInfo.StandardErrorEncoding = Encoding.UTF8; + + var result = string.Empty; + + // Create a StreamWriter to write the output to a log file + try + { + //using var outputFile = new StreamWriter("error.log", append: true); + process.ErrorDataReceived += (sender, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + { + //Debug.WriteLine($"STDERR = {e.Data}"); + BackendSetupSubStatusQueueEnqueue(e.Data); + } + }; + + process.OutputDataReceived += (sender, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + { + result = e.Data; + //Debug.WriteLine($"STDOUT = {e.Data}"); + BackendSetupSubStatusQueueEnqueue(e.Data); + } + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); // Start asynchronous reading of the output + await process.WaitForExitAsync(); + } + catch (IOException) { } + } + + return []; + } + public void CheckAndDoBackup() { Task.Run(() => @@ -916,7 +1075,7 @@ public void CheckAndDoBackup() public void ResetCurrentWorkflow() { - if (CurrentWorkflow != null) + if (CurrentWorkflow != null) { var lines = JsonConvert.SerializeObject(Workflows[0], NewtonsoftJsonSuspensionDriver.Settings); var workflow = JsonConvert.DeserializeObject(lines, NewtonsoftJsonSuspensionDriver.Settings); @@ -956,15 +1115,15 @@ public async Task> PopulateDevicesAsync(string? searchText, var devices = JsonConvert.DeserializeObject>(response, NewtonsoftJsonSuspensionDriver.Settings); if (devices != null) { - foreach (var device in devices) + foreach (var device in devices) { DisplayDeviceMap[device.ToString()] = device; - + } return devices.ToList(); } } - catch(Exception ex) + catch (Exception ex) { Debug.WriteLine(ex); } @@ -976,11 +1135,11 @@ public async Task CheckForUpdates() { try { - if (IsInstalled) + if (_updateManagerService.IsInstalled) { await Task.Run(async () => { - _update = await _um.CheckForUpdatesAsync().ConfigureAwait(true); + _update = await _updateManagerService.CheckForUpdatesAsync().ConfigureAwait(true); }); UpdateStatus(); @@ -1004,7 +1163,7 @@ public async Task DownloadUpdate() if (_update != null) { ShowDownloadButton = false; - await _um.DownloadUpdatesAsync(_update, Progress).ConfigureAwait(true); + await _updateManagerService.DownloadUpdatesAsync(_update, Progress).ConfigureAwait(true); UpdateStatus(); } } @@ -1019,7 +1178,7 @@ public void ApplyUpdate() if (_update != null) { ShowApplyButton = false; - _um.ApplyUpdatesAndRestart(_update); + _updateManagerService.ApplyUpdatesAndRestart(_update); } } @@ -1035,7 +1194,7 @@ private void UpdateStatus() ShowDownloadButton = true; ShowCheckUpdateButton = false; - if (_um.IsUpdatePendingRestart) + if (_updateManagerService.IsUpdatePendingRestart) { UpdateStatusText = $"Update ready, pending restart to install version: {_update.TargetFullRelease.Version}"; ShowDownloadButton = false; @@ -1058,13 +1217,11 @@ private void Progress(int percent) } -#pragma warning disable CA1822 // Mark members as static public async void OpenModelsDirectory() -#pragma warning restore CA1822 // Mark members as static { await Task.Run(() => { - Process.Start("explorer.exe", UpscaleChain.PthPath); + Process.Start("explorer.exe", _pythonService.ModelsDirectory); }); } } @@ -1182,7 +1339,8 @@ public int WorkflowIndex public string WorkflowIcon => $"Numeric{WorkflowIndex}Circle"; - public bool ActiveWorkflow { + public bool ActiveWorkflow + { get { Debug.WriteLine($"ActiveWorkflow {WorkflowIndex} == {Vm?.SelectedWorkflowIndex}; {Vm == null}"); @@ -1455,9 +1613,10 @@ public string DisplayDevice } [DataMember] - public int DisplayDeviceWidth { - get - { + public int DisplayDeviceWidth + { + get + { if (Vm != null && DisplayDevice != null) { Vm.DisplayDeviceMap.TryGetValue(DisplayDevice, out var displayDevice); @@ -1468,7 +1627,7 @@ public int DisplayDeviceWidth { } return 0; - } + } } [DataMember] @@ -1680,8 +1839,12 @@ public void Validate() [DataContract] public class UpscaleChain : ReactiveObject { - public UpscaleChain() + IPythonService _pythonService; + + public UpscaleChain(IPythonService? pythonService = null) { + _pythonService = pythonService ?? Locator.Current.GetService()!; + this.WhenAnyValue(x => x.Vm).Subscribe(x => { sub?.Dispose(); @@ -1811,33 +1974,7 @@ public double? ResizeFactorBeforeUpscale set => this.RaiseAndSetIfChanged(ref _resizeFactorBeforeUpscale, value ?? 100); } - public static string PthPath => Path.GetFullPath(@".\chaiNNer\models"); - - public static AvaloniaList AllModels => GetAllModels(); - - public static AvaloniaList GetAllModels() - { - try - { - var models = new AvaloniaList(Directory.GetFiles(PthPath).Where(filename => - Path.GetExtension(filename).Equals(".pth", StringComparison.CurrentCultureIgnoreCase) || - Path.GetExtension(filename).Equals(".pt", StringComparison.CurrentCultureIgnoreCase) || - Path.GetExtension(filename).Equals(".ckpt", StringComparison.CurrentCultureIgnoreCase) || - Path.GetExtension(filename).Equals(".safetensors", StringComparison.CurrentCultureIgnoreCase) -) - .Select(filename => Path.GetFileName(filename)) - .Order().ToList()); - - Debug.WriteLine($"GetAllModels: {models.Count}"); - - return models; - } - catch (DirectoryNotFoundException) - { - Debug.WriteLine($"GetAllModels: DirectoryNotFoundException"); - return []; - } - } + public AvaloniaList AllModels => _pythonService.AllModels; private string[] _tileSizes = [ "Auto (Estimate)", diff --git a/MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs b/MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs index e170474..e8bf050 100644 --- a/MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs +++ b/MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs @@ -1,5 +1,4 @@ using ReactiveUI; -using System.Runtime.Serialization; namespace MangaJaNaiConverterGui.ViewModels { diff --git a/MangaJaNaiConverterGui/Views/MainWindow.axaml b/MangaJaNaiConverterGui/Views/MainWindow.axaml index 53ebbe3..8e658aa 100644 --- a/MangaJaNaiConverterGui/Views/MainWindow.axaml +++ b/MangaJaNaiConverterGui/Views/MainWindow.axaml @@ -35,10 +35,10 @@ --> + @@ -136,8 +136,8 @@ - - + + @@ -443,8 +443,8 @@ - - + + @@ -605,7 +605,7 @@ - + @@ -663,6 +663,18 @@ + + + + + + + + + + + + @@ -677,7 +689,12 @@ - Performing first time setup. This may take several minutes... + + + + + + diff --git a/MangaJaNaiConverterGui/Views/MainWindow.axaml.cs b/MangaJaNaiConverterGui/Views/MainWindow.axaml.cs index c1274cf..1a8ffb6 100644 --- a/MangaJaNaiConverterGui/Views/MainWindow.axaml.cs +++ b/MangaJaNaiConverterGui/Views/MainWindow.axaml.cs @@ -1,24 +1,19 @@ -using MangaJaNaiConverterGui.ViewModels; using Avalonia; using Avalonia.Controls; using Avalonia.Input; using Avalonia.Interactivity; +using Avalonia.Layout; using Avalonia.Markup.Xaml; +using Avalonia.Media; using Avalonia.Platform.Storage; -using Avalonia.ReactiveUI; -using ReactiveUI; +using FluentAvalonia.UI.Controls; +using FluentAvalonia.UI.Windowing; +using MangaJaNaiConverterGui.ViewModels; +using Material.Icons.Avalonia; using System; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; -using FluentAvalonia.UI.Windowing; -using Avalonia.Layout; -using Avalonia.Media; -using Material.Icons.Avalonia; -using Velopack; -using FluentAvalonia.UI.Controls; -using System.Threading; namespace MangaJaNaiConverterGui.Views { @@ -82,10 +77,8 @@ private async void MainWindow_Closing(object? sender, WindowClosingEventArgs e) private void ConsoleScrollViewer_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { - if (e.Property.Name == "Offset") + if (e.Property.Name == "Offset" && sender is ScrollViewer consoleScrollViewer) { - var consoleScrollViewer = this.FindControl("ConsoleScrollViewer"); - if (e.NewValue is Vector newVector) { _autoScrollConsole = newVector.Y == consoleScrollViewer?.ScrollBarMaximum.Y; @@ -96,14 +89,16 @@ private void ConsoleScrollViewer_PropertyChanged(object? sender, AvaloniaPropert private void ConsoleTextBlock_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e) { - if (e.Property.Name == "Text") + if (e.Property.Name == "Text" && sender is TextBlock textBlock) { - var consoleScrollViewer = this.FindControl("ConsoleScrollViewer"); - if (consoleScrollViewer != null) + if (textBlock.Parent is ScrollViewer consoleScrollViewer) { - if (_autoScrollConsole) + if (consoleScrollViewer != null) { - consoleScrollViewer.ScrollToEnd(); + if (_autoScrollConsole) + { + consoleScrollViewer.ScrollToEnd(); + } } } } @@ -237,7 +232,7 @@ private async void OpenInputFolderButtonClick(object? sender, RoutedEventArgs e) if (files.Count >= 1) { - vm.CurrentWorkflow.InputFolderPath = files[0].TryGetLocalPath() ?? ""; + vm.CurrentWorkflow.InputFolderPath = files[0].TryGetLocalPath() ?? ""; } } } @@ -341,7 +336,7 @@ private async void ResetWorkflow(object? sender, RoutedEventArgs e) { if (DataContext is MainWindowViewModel vm) { - + var td = new TaskDialog { Title = "Confirm Workflow Reset", @@ -375,7 +370,7 @@ await Task.Run(() => td.XamlRoot = VisualRoot as Visual; _ = await td.ShowAsync(); - + } } diff --git a/MangaJaNaiConverterGui/chaiNNer/ImageMagick/Custom Gray Gamma 1.0.icc b/MangaJaNaiConverterGui/backend/ImageMagick/Custom Gray Gamma 1.0.icc similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/ImageMagick/Custom Gray Gamma 1.0.icc rename to MangaJaNaiConverterGui/backend/ImageMagick/Custom Gray Gamma 1.0.icc diff --git a/MangaJaNaiConverterGui/chaiNNer/ImageMagick/Custom RGB Gamma 1.0.icc b/MangaJaNaiConverterGui/backend/ImageMagick/Custom RGB Gamma 1.0.icc similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/ImageMagick/Custom RGB Gamma 1.0.icc rename to MangaJaNaiConverterGui/backend/ImageMagick/Custom RGB Gamma 1.0.icc diff --git a/MangaJaNaiConverterGui/chaiNNer/ImageMagick/Dot Gain 20%.icc b/MangaJaNaiConverterGui/backend/ImageMagick/Dot Gain 20%.icc similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/ImageMagick/Dot Gain 20%.icc rename to MangaJaNaiConverterGui/backend/ImageMagick/Dot Gain 20%.icc diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/__init__.py b/MangaJaNaiConverterGui/backend/src/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/__init__.py rename to MangaJaNaiConverterGui/backend/src/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/__init__.py b/MangaJaNaiConverterGui/backend/src/api/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/__init__.py rename to MangaJaNaiConverterGui/backend/src/api/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/api.py b/MangaJaNaiConverterGui/backend/src/api/api.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/api.py rename to MangaJaNaiConverterGui/backend/src/api/api.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/group.py b/MangaJaNaiConverterGui/backend/src/api/group.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/group.py rename to MangaJaNaiConverterGui/backend/src/api/group.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/input.py b/MangaJaNaiConverterGui/backend/src/api/input.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/input.py rename to MangaJaNaiConverterGui/backend/src/api/input.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/lazy.py b/MangaJaNaiConverterGui/backend/src/api/lazy.py similarity index 86% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/lazy.py rename to MangaJaNaiConverterGui/backend/src/api/lazy.py index 3506a38..6aa9657 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/lazy.py +++ b/MangaJaNaiConverterGui/backend/src/api/lazy.py @@ -49,6 +49,7 @@ def __init__(self, factory: Callable[[], T]): self._factory = _to_result(factory) self._value: _Result[T] | None = None self._evaluating = False + self._eval_time = 0 @staticmethod def ready(value: T) -> Lazy[T]: @@ -75,12 +76,17 @@ def supplier() -> T: @property def has_value(self) -> bool: """Returns True if the value has been computed, otherwise False.""" - return self._value is not None + return self._value is not None and self._value.is_ok @property def has_error(self) -> bool: """Returns True if the value has been computed and it errored instead, otherwise False.""" - return self._value is not None + return self._value is not None and not self._value.is_ok + + @property + def evaluation_time(self) -> float: + """The time in seconds that it took to evaluate the value. If the value is not computed, returns 0.""" + return self._eval_time @property def value(self) -> T: @@ -94,7 +100,9 @@ def value(self) -> T: else: self._evaluating = True try: + start = time.time() self._value = self._factory() + self._eval_time = time.time() - start finally: self._evaluating = False diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_check.py b/MangaJaNaiConverterGui/backend/src/api/node_check.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_check.py rename to MangaJaNaiConverterGui/backend/src/api/node_check.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_context.py b/MangaJaNaiConverterGui/backend/src/api/node_context.py similarity index 92% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_context.py rename to MangaJaNaiConverterGui/backend/src/api/node_context.py index 0e55074..270cd52 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_context.py +++ b/MangaJaNaiConverterGui/backend/src/api/node_context.py @@ -1,7 +1,7 @@ import time from abc import ABC, abstractmethod from pathlib import Path -from typing import Literal +from typing import Callable, Literal from .settings import SettingsParser @@ -146,3 +146,11 @@ def storage_dir(self) -> Path: This directory persists between node executions, and its contents are shared between different nodes. """ + + @abstractmethod + def add_cleanup(self, fn: Callable[[], None]) -> None: + """ + Registers a function that will be called when the chain execution is finished. + + Registering the same function (object) twice will only result in the function being called once. + """ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_data.py b/MangaJaNaiConverterGui/backend/src/api/node_data.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/node_data.py rename to MangaJaNaiConverterGui/backend/src/api/node_data.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/output.py b/MangaJaNaiConverterGui/backend/src/api/output.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/output.py rename to MangaJaNaiConverterGui/backend/src/api/output.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/settings.py b/MangaJaNaiConverterGui/backend/src/api/settings.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/settings.py rename to MangaJaNaiConverterGui/backend/src/api/settings.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api/types.py b/MangaJaNaiConverterGui/backend/src/api/types.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/api/types.py rename to MangaJaNaiConverterGui/backend/src/api/types.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/device_list.py b/MangaJaNaiConverterGui/backend/src/device_list.py similarity index 91% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/device_list.py rename to MangaJaNaiConverterGui/backend/src/device_list.py index 1495077..6efa1de 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/device_list.py +++ b/MangaJaNaiConverterGui/backend/src/device_list.py @@ -5,4 +5,4 @@ device_name = torch.cuda.get_device_properties(i).name gpu_list.append(device_name) -print(gpu_list) \ No newline at end of file +print(gpu_list) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/gpu.py b/MangaJaNaiConverterGui/backend/src/gpu.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/gpu.py rename to MangaJaNaiConverterGui/backend/src/gpu.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/navi.py b/MangaJaNaiConverterGui/backend/src/navi.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/navi.py rename to MangaJaNaiConverterGui/backend/src/navi.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/base_input.py b/MangaJaNaiConverterGui/backend/src/nodes/base_input.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/base_input.py rename to MangaJaNaiConverterGui/backend/src/nodes/base_input.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/base_output.py b/MangaJaNaiConverterGui/backend/src/nodes/base_output.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/base_output.py rename to MangaJaNaiConverterGui/backend/src/nodes/base_output.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/condition.py b/MangaJaNaiConverterGui/backend/src/nodes/condition.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/condition.py rename to MangaJaNaiConverterGui/backend/src/nodes/condition.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/group.py b/MangaJaNaiConverterGui/backend/src/nodes/group.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/group.py rename to MangaJaNaiConverterGui/backend/src/nodes/group.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/groups.py b/MangaJaNaiConverterGui/backend/src/nodes/groups.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/groups.py rename to MangaJaNaiConverterGui/backend/src/nodes/groups.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/blend.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/blend.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/blend.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/blend.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/color/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/color/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/color.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/color/color.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/color.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/color/color.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/convert.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/convert.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/convert_data.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_data.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/convert_data.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_data.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/convert_model.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_model.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/convert_model.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_model.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/image_formats.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/image_formats.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/image_formats.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/image_formats.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/image_op.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/image_op.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/image_op.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/image_op.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/image_utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/image_utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/image_utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/image_utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/auto_split.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/auto_split.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/auto_split.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/auto_split.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/model.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/model.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/model.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/model.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/np_tensor_utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/np_tensor_utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/np_tensor_utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/np_tensor_utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/onnx_to_ncnn.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/onnx_to_ncnn.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/onnx_to_ncnn.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/onnx_to_ncnn.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/session.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/session.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/session.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/session.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/tensorproto_utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/tensorproto_utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/tensorproto_utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/tensorproto_utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pil_utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pil_utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pil_utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pil_utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/DAT.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/DAT.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/DAT.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/DAT.py index 0bcc26e..cbf05c8 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/DAT.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/DAT.py @@ -8,7 +8,6 @@ import torch.utils.checkpoint as checkpoint from einops import rearrange from einops.layers.torch import Rearrange -from torch import Tensor from torch.nn import functional as F from .timm.drop import DropPath @@ -1070,9 +1069,9 @@ def __init__(self, state_dict): # ------------------------- 2, Deep Feature Extraction ------------------------- # self.num_layers = len(depth) self.use_chk = use_chk - self.num_features = ( - self.embed_dim - ) = embed_dim # num_features for consistency with other models + self.num_features = self.embed_dim = ( + embed_dim # num_features for consistency with other models + ) heads = num_heads self.before_RG = nn.Sequential( diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/HAT.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/HAT.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/HAT.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/HAT.py index 6694742..696b25c 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/HAT.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/HAT.py @@ -1100,7 +1100,9 @@ def __init__( drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[ - sum(depths[:i_layer]) : sum(depths[: i_layer + 1]) # type: ignore + sum(depths[:i_layer]) : sum( + depths[: i_layer + 1] + ) # type: ignore ], # no impact on SR results norm_layer=norm_layer, downsample=None, diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-DAT b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-DAT similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-DAT rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-DAT diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-ESRGAN b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-ESRGAN similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-ESRGAN rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-ESRGAN diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-HAT b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-HAT similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-HAT rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-HAT diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-RealESRGAN b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-RealESRGAN similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-RealESRGAN rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-RealESRGAN diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SCUNet b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SCUNet similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SCUNet rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SCUNet diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SPSR b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SPSR similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SPSR rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SPSR diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SRFormer b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SRFormer similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SRFormer rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SRFormer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwiftSRGAN b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwiftSRGAN similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwiftSRGAN rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwiftSRGAN diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-Swin2SR b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-Swin2SR similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-Swin2SR rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-Swin2SR diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwinIR b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwinIR similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwinIR rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-SwinIR diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-lama b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-lama similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-lama rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-lama diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-mat b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-mat similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LICENSE-mat rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LICENSE-mat diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LaMa.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LaMa.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LaMa.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LaMa.py index a781f3e..5bd4d08 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/LaMa.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/LaMa.py @@ -7,8 +7,6 @@ # original implementation https://github.com/pkumivision/FFC/blob/main/model_zoo/ffc.py # paper https://proceedings.neurips.cc/paper/2020/file/2fd5d41ec6cfab47e32164d5624269b1-Paper.pdf -from typing import List - import torch import torch.nn as nn import torch.nn.functional as F diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/MAT.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/MAT.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/MAT.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/MAT.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/ChannelAttention.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/ChannelAttention.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/ChannelAttention.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/ChannelAttention.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/LICENSE b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/LICENSE similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/LICENSE rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/LICENSE diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSA.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSA.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSA.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSA.py index d7a1296..9b9d91c 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSA.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSA.py @@ -12,7 +12,7 @@ import torch import torch.nn.functional as F -from einops import rearrange, repeat +from einops import rearrange from einops.layers.torch import Rearrange, Reduce from torch import einsum, nn diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSAG.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSAG.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSAG.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OSAG.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OmniSR.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OmniSR.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/OmniSR.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/OmniSR.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/esa.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/esa.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/esa.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/esa.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/layernorm.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/layernorm.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/layernorm.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/layernorm.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/pixelshuffle.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/pixelshuffle.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/OmniSR/pixelshuffle.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/OmniSR/pixelshuffle.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/RRDB.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/RRDB.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/RRDB.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/RRDB.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SCUNet.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SCUNet.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SCUNet.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SCUNet.py index 904a52d..39ac9a0 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SCUNet.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SCUNet.py @@ -11,7 +11,6 @@ from einops import rearrange from einops.layers.torch import Rearrange -from nodes.utils.utils import round_half_up from .timm.drop import DropPath from .timm.weight_init import trunc_normal_ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SPSR.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SPSR.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SPSR.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SPSR.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SRFormer.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SRFormer.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SRFormer.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SRFormer.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SRVGG.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SRVGG.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SRVGG.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SRVGG.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SwiftSRGAN.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SwiftSRGAN.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SwiftSRGAN.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SwiftSRGAN.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/Swin2SR.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/Swin2SR.py similarity index 97% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/Swin2SR.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/Swin2SR.py index cb57ecf..96e2c48 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/Swin2SR.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/Swin2SR.py @@ -109,7 +109,9 @@ def __init__( self.pretrained_window_size = pretrained_window_size self.num_heads = num_heads - self.logit_scale = nn.Parameter(torch.log(10 * torch.ones((num_heads, 1, 1))), requires_grad=True) # type: ignore + self.logit_scale = nn.Parameter( + torch.log(10 * torch.ones((num_heads, 1, 1))), requires_grad=True + ) # type: ignore # mlp to generate continuous relative position bias self.cpb_mlp = nn.Sequential( @@ -184,7 +186,13 @@ def forward(self, x, mask=None): B_, N, C = x.shape qkv_bias = None if self.q_bias is not None: - qkv_bias = torch.cat((self.q_bias, torch.zeros_like(self.v_bias, requires_grad=False), self.v_bias)) # type: ignore + qkv_bias = torch.cat( + ( + self.q_bias, + torch.zeros_like(self.v_bias, requires_grad=False), + self.v_bias, + ) + ) # type: ignore qkv = F.linear(input=x, weight=self.qkv.weight, bias=qkv_bias) qkv = qkv.reshape(B_, N, 3, self.num_heads, -1).permute(2, 0, 3, 1, 4) q, k, v = ( @@ -204,7 +212,9 @@ def forward(self, x, mask=None): relative_position_bias_table = self.cpb_mlp(self.relative_coords_table).view( -1, self.num_heads ) - relative_position_bias = relative_position_bias_table[self.relative_position_index.view(-1)].view( # type: ignore + relative_position_bias = relative_position_bias_table[ + self.relative_position_index.view(-1) + ].view( # type: ignore self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], -1, @@ -600,7 +610,10 @@ def __init__( super().__init__() img_size = to_2tuple(img_size) patch_size = to_2tuple(patch_size) - patches_resolution = [img_size[0] // patch_size[0], img_size[1] // patch_size[1]] # type: ignore + patches_resolution = [ + img_size[0] // patch_size[0], + img_size[1] // patch_size[1], + ] # type: ignore self.img_size = img_size self.patch_size = patch_size self.patches_resolution = patches_resolution @@ -610,7 +623,10 @@ def __init__( self.embed_dim = embed_dim self.proj = nn.Conv2d( - in_chans, embed_dim, kernel_size=patch_size, stride=patch_size # type: ignore + in_chans, + embed_dim, + kernel_size=patch_size, + stride=patch_size, # type: ignore ) if norm_layer is not None: self.norm = norm_layer(embed_dim) @@ -629,7 +645,13 @@ def forward(self, x): def flops(self): Ho, Wo = self.patches_resolution - flops = Ho * Wo * self.embed_dim * self.in_chans * (self.patch_size[0] * self.patch_size[1]) # type: ignore + flops = ( + Ho + * Wo + * self.embed_dim + * self.in_chans + * (self.patch_size[0] * self.patch_size[1]) + ) # type: ignore if self.norm is not None: flops += Ho * Wo * self.embed_dim return flops @@ -761,7 +783,10 @@ def __init__( super().__init__() img_size = to_2tuple(img_size) patch_size = to_2tuple(patch_size) - patches_resolution = [img_size[0] // patch_size[0], img_size[1] // patch_size[1]] # type: ignore + patches_resolution = [ + img_size[0] // patch_size[0], + img_size[1] // patch_size[1], + ] # type: ignore self.img_size = img_size self.patch_size = patch_size self.patches_resolution = patches_resolution @@ -1091,7 +1116,9 @@ def __init__( # absolute position embedding if self.ape: - self.absolute_pos_embed = nn.Parameter(torch.zeros(1, num_patches, embed_dim)) # type: ignore + self.absolute_pos_embed = nn.Parameter( + torch.zeros(1, num_patches, embed_dim) + ) # type: ignore trunc_normal_(self.absolute_pos_embed, std=0.02) self.pos_drop = nn.Dropout(p=drop_rate) @@ -1360,7 +1387,11 @@ def forward(self, x): elif self.upsampler == "pixelshuffle_hf": x_out = x_out / self.img_range + self.mean # type: ignore - return x_out[:, :, : H * self.upscale, : W * self.upscale], x[:, :, : H * self.upscale, : W * self.upscale], x_hf[:, :, : H * self.upscale, : W * self.upscale] # type: ignore + return ( + x_out[:, :, : H * self.upscale, : W * self.upscale], + x[:, :, : H * self.upscale, : W * self.upscale], + x_hf[:, :, : H * self.upscale, : W * self.upscale], + ) # type: ignore else: return x[:, :, : H * self.upscale, : W * self.upscale] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SwinIR.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SwinIR.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SwinIR.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SwinIR.py index 4ab1046..840cf12 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/SwinIR.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/SwinIR.py @@ -1059,7 +1059,9 @@ def __init__( drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[ - sum(depths[:i_layer]) : sum(depths[: i_layer + 1]) # type: ignore + sum(depths[:i_layer]) : sum( + depths[: i_layer + 1] + ) # type: ignore ], # no impact on SR results norm_layer=norm_layer, downsample=None, diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/block.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/block.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/block.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/block.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-GFPGAN b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-GFPGAN similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-GFPGAN rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-GFPGAN diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-RestoreFormer b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-RestoreFormer similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-RestoreFormer rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-RestoreFormer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-codeformer b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-codeformer similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-codeformer rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/LICENSE-codeformer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/arcface_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/arcface_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/arcface_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/arcface_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/codeformer.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/codeformer.py similarity index 98% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/codeformer.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/codeformer.py index 327b5c3..b4187c4 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/codeformer.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/codeformer.py @@ -4,6 +4,7 @@ https://github.com/samb-t/unleashing-transformers/blob/master/models/vqgan.py This verison of the arch specifically was gathered from an old version of GFPGAN. If this is a problem, please contact me. """ + import math from typing import Optional @@ -550,15 +551,27 @@ def __init__(self, in_channels, out_channels=None): self.out_channels = in_channels if out_channels is None else out_channels self.norm1 = normalize(in_channels) self.conv1 = nn.Conv2d( - in_channels, out_channels, kernel_size=3, stride=1, padding=1 # type: ignore + in_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, # type: ignore ) self.norm2 = normalize(out_channels) self.conv2 = nn.Conv2d( - out_channels, out_channels, kernel_size=3, stride=1, padding=1 # type: ignore + out_channels, + out_channels, + kernel_size=3, + stride=1, + padding=1, # type: ignore ) if self.in_channels != self.out_channels: self.conv_out = nn.Conv2d( - in_channels, out_channels, kernel_size=1, stride=1, padding=0 # type: ignore + in_channels, + out_channels, + kernel_size=1, + stride=1, + padding=0, # type: ignore ) def forward(self, x_in): @@ -762,7 +775,8 @@ def forward(self, x, weight=0.5, **kwargs): soft_one_hot = F.softmax(logits, dim=2) _, top_idx = torch.topk(soft_one_hot, 1, dim=2) quant_feat = self.quantize.get_codebook_feat( - top_idx, shape=[x.shape[0], 16, 16, 256] # type: ignore + top_idx, + shape=[x.shape[0], 16, 16, 256], # type: ignore ) # preserve gradients # quant_feat = lq_feat + (quant_feat - lq_feat).detach() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/fused_act.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/fused_act.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/fused_act.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/fused_act.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/gfpgan_bilinear_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/gfpgan_bilinear_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/gfpgan_bilinear_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/gfpgan_bilinear_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_clean_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_clean_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_clean_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/gfpganv1_clean_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/restoreformer_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/restoreformer_arch.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/restoreformer_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/restoreformer_arch.py index 4492260..ef6dfd5 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/restoreformer_arch.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/restoreformer_arch.py @@ -1,7 +1,7 @@ # pylint: skip-file # type: ignore -"""Modified from https://github.com/wzhouxiff/RestoreFormer -""" +"""Modified from https://github.com/wzhouxiff/RestoreFormer""" + import numpy as np import torch import torch.nn as nn @@ -167,7 +167,7 @@ def __init__( out_channels=None, conv_shortcut=False, dropout, - temb_channels=512 + temb_channels=512, ): super().__init__() self.in_channels = in_channels @@ -305,7 +305,7 @@ def __init__( double_z=True, enable_mid=True, head_size=1, - **ignore_kwargs + **ignore_kwargs, ): super().__init__() self.ch = ch @@ -430,7 +430,7 @@ def __init__( give_pre_end=False, enable_mid=True, head_size=1, - **ignorekwargs + **ignorekwargs, ): super().__init__() self.ch = ch @@ -557,7 +557,7 @@ def __init__( give_pre_end=False, enable_mid=True, head_size=1, - **ignorekwargs + **ignorekwargs, ): super().__init__() self.ch = ch diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_bilinear_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_bilinear_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_bilinear_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_bilinear_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_clean_arch.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_clean_arch.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_clean_arch.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/stylegan2_clean_arch.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/upfirdn2d.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/upfirdn2d.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/upfirdn2d.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/upfirdn2d.py index 4ea4541..c8e85a5 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/face/upfirdn2d.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/face/upfirdn2d.py @@ -2,7 +2,6 @@ # type: ignore # modify from https://github.com/rosinality/stylegan2-pytorch/blob/master/op/upfirdn2d.py # noqa:E501 -import os import torch from torch.autograd import Function diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/mat/utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/mat/utils.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/mat/utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/mat/utils.py index 1e9445a..abd21c3 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/mat/utils.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/mat/utils.py @@ -464,9 +464,7 @@ def _conv2d_wrapper( out_channels, in_channels_per_group, kh, kw = _get_weight_shape(w) # Flip weight if requested. - if ( - not flip_weight - ): # conv2d() actually performs correlation (flip_weight=True) not convolution (flip_weight=False). + if not flip_weight: # conv2d() actually performs correlation (flip_weight=True) not convolution (flip_weight=False). w = w.flip([2, 3]) # Workaround performance pitfall in cuDNN 8.0.5, triggered when using diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/LICENSE b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/LICENSE similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/LICENSE rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/LICENSE diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/drop.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/drop.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/drop.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/drop.py index 14f0da9..0c713a5 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/drop.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/drop.py @@ -1,4 +1,4 @@ -""" DropBlock, DropPath +"""DropBlock, DropPath PyTorch implementations of DropBlock and DropPath (Stochastic Depth) regularization layers. @@ -14,6 +14,7 @@ Hacked together by / Copyright 2020 Ross Wightman """ + import torch import torch.nn as nn import torch.nn.functional as F diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/helpers.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/helpers.py similarity index 96% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/helpers.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/helpers.py index cdafee0..b5ecc74 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/helpers.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/helpers.py @@ -1,6 +1,7 @@ -""" Layer/Module Helpers +"""Layer/Module Helpers Hacked together by / Copyright 2020 Ross Wightman """ + import collections.abc from itertools import repeat diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/weight_init.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/weight_init.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/timm/weight_init.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/architecture/timm/weight_init.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/auto_split.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/auto_split.py similarity index 78% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/auto_split.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/auto_split.py index ddcabe5..12ea8d4 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/auto_split.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/auto_split.py @@ -48,6 +48,27 @@ def _rgb_to_bgr(t: torch.Tensor) -> torch.Tensor: return t +def _into_tensor( + img: np.ndarray, device: torch.device, dtype: torch.dtype +) -> torch.Tensor: + img = np.ascontiguousarray(img) + writeable = img.flags.writeable + try: + if not writeable and device == torch.device("cpu"): + img = np.copy(img) + else: + # since we are going to copy the image to the GPU, we can skip the copy here + try: + img.flags.writeable = True + except Exception: + # Some arrays cannot be made writeable, and we need to copy them + img = np.copy(img) + input_tensor = torch.from_numpy(img).to(device, dtype) + return input_tensor + finally: + img.flags.writeable = writeable + + @torch.inference_mode() def pytorch_auto_split( img: np.ndarray, @@ -58,7 +79,8 @@ def pytorch_auto_split( progress: Progress, ) -> np.ndarray: dtype = torch.float16 if use_fp16 else torch.float32 - model = model.to(device, dtype) + if model.dtype != dtype or model.device != device: + model = model.to(device, dtype) def upscale(img: np.ndarray, _: object): progress.check_aborted() @@ -71,10 +93,7 @@ def upscale(img: np.ndarray, _: object): input_tensor = None try: # convert to tensor - img = np.ascontiguousarray(img) - if not img.flags.writeable: - img = np.copy(img) - input_tensor = torch.from_numpy(img).to(device, dtype) + input_tensor = _into_tensor(img, device, dtype) input_tensor = _rgb_to_bgr(input_tensor) input_tensor = _into_batched_form(input_tensor) @@ -104,7 +123,4 @@ def upscale(img: np.ndarray, _: object): # Re-raise the exception if not an OOM error raise - try: - return auto_split(img, upscale, tiler) - finally: - safe_cuda_cache_empty() + return auto_split(img, upscale, tiler) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/convert_to_onnx_impl.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/convert_to_onnx_impl.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/convert_to_onnx_impl.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/convert_to_onnx_impl.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/model_loading.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/model_loading.py similarity index 98% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/model_loading.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/model_loading.py index 738fc7a..5fafb45 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/model_loading.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/model_loading.py @@ -24,7 +24,7 @@ class UnsupportedModel(Exception): def load_state_dict(state_dict) -> PyTorchModel: - logger.debug(f"Loading state dict into pytorch model arch") + logger.debug("Loading state dict into pytorch model arch") state_dict_keys = list(state_dict.keys()) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/LICENSE b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/LICENSE similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/LICENSE rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/LICENSE diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/auto_split.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/auto_split.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/auto_split.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/auto_split.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/pix_transform.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/pix_transform.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/pix_transform_net.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform_net.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/pix_transform/pix_transform_net.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform_net.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/rife/IFNet_HDv3_v4_14_align.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/IFNet_HDv3_v4_14_align.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/rife/IFNet_HDv3_v4_14_align.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/IFNet_HDv3_v4_14_align.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/rife/warplayer.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/warplayer.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/rife/warplayer.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/warplayer.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/types.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/types.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/types.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/types.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/utils.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/resize.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/resize.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/resize.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/resize.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/auto_split.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split.py similarity index 98% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/auto_split.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split.py index 864d3e6..088f41d 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/auto_split.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split.py @@ -63,7 +63,7 @@ def _exact_split( overlap: int, ) -> np.ndarray: h, w, c = get_h_w_c(img) - logger.info( + logger.debug( f"Exact size split image ({w}x{h}px @ {c}) with exact tile size {starting_tile_size[0]}x{starting_tile_size[1]}px." ) @@ -108,7 +108,7 @@ def _max_split( img_region = Region(0, 0, w, h) max_tile_size = starting_tile_size - logger.info( + logger.debug( f"Auto split image ({w}x{h}px @ {c}) with initial tile size {max_tile_size}." ) @@ -121,7 +121,7 @@ def _max_split( # the image was too large max_tile_size = split_tile_size(max_tile_size) - logger.info( + logger.warn( f"Unable to upscale the whole image at once. Reduced tile size to {max_tile_size}." ) @@ -151,7 +151,7 @@ def _max_split( tile_size_x = math.ceil(w / tile_count_x) tile_size_y = math.ceil(h / tile_count_y) - logger.info( + logger.debug( f"Currently {tile_count_x}x{tile_count_y} tiles each {tile_size_x}x{tile_size_y}px." ) @@ -180,7 +180,7 @@ def _max_split( new_tile_size_y = math.ceil(h / new_tile_count_y) start_y = (y * tile_size_x) // new_tile_size_y - logger.info( + logger.debug( f"Split occurred. New tile size is {max_tile_size}. Starting at row {start_y}." ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/auto_split_tiles.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split_tiles.py similarity index 98% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/auto_split_tiles.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split_tiles.py index 4a2a76a..1f906a6 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/auto_split_tiles.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split_tiles.py @@ -25,7 +25,7 @@ def estimate_tile_size( required_mem = f"{mem_required_estimation/GB_AMT:.2f}" budget_mem = f"{budget/GB_AMT:.2f}" - logger.info( + logger.debug( f"Estimating memory required: {required_mem} GB, {budget_mem} GB free." f" Estimated tile size: {tile_size}" ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/convenient_upscale.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/convenient_upscale.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/convenient_upscale.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/convenient_upscale.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/custom_scale.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/custom_scale.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/custom_scale.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/custom_scale.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/exact_split.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/exact_split.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/exact_split.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/exact_split.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/grayscale.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/grayscale.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/grayscale.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/grayscale.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/passthrough.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/passthrough.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/passthrough.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/passthrough.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/tile_blending.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tile_blending.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/tile_blending.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tile_blending.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/tiler.py b/MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tiler.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/tiler.py rename to MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tiler.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/node_cache.py b/MangaJaNaiConverterGui/backend/src/nodes/node_cache.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/node_cache.py rename to MangaJaNaiConverterGui/backend/src/nodes/node_cache.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/file_inputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/file_inputs.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/file_inputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/file_inputs.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/generic_inputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/generic_inputs.py similarity index 92% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/generic_inputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/generic_inputs.py index 594f66c..d172fbf 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/generic_inputs.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/generic_inputs.py @@ -147,8 +147,8 @@ def wrap_with_conditional_group(self): return group("conditional", {"condition": condition})(self) -class BoolInput(DropDownInput[bool]): - def __init__(self, label: str, default: bool = True, icon: str | None = None): +class _BoolEnumInput(DropDownInput[bool]): + def __init__(self, label: str, *, default: bool = True, icon: str | None = None): super().__init__( input_type="bool", label=label, @@ -175,6 +175,34 @@ def enforce(self, value: object) -> bool: return bool(value) +class _BoolGenericInput(BaseInput[bool]): + def __init__(self, label: str): + super().__init__(input_type="bool", label=label) + self.associated_type = bool + + def enforce(self, value: object) -> bool: + if isinstance(value, bool): + return value + if isinstance(value, int): + return bool(value) + + raise ValueError( + f"The value of input '{self.label}' should have been either True or False." + ) + + +def BoolInput( + label: str, + *, + default: bool = True, + icon: str | None = None, + has_handle: bool = False, +): + if has_handle: + return _BoolGenericInput(label) + return _BoolEnumInput(label, default=default, icon=icon) + + E = TypeVar("E", bound=Enum) @@ -203,6 +231,7 @@ def __init__( self, enum: type[E], label: str | None = None, + *, default: E | None = None, type_name: str | None = None, option_labels: dict[E, str] | None = None, @@ -283,6 +312,7 @@ class TextInput(BaseInput[str]): def __init__( self, label: str, + *, has_handle: bool = True, min_length: int = 0, max_length: int | None = None, @@ -310,8 +340,8 @@ def __init__( self.invalid_pattern = invalid_pattern if default is not None: - assert default != "" - assert min_length < len(default) + assert default != "" or allow_empty_string + assert min_length <= len(default) assert max_length is None or len(default) < max_length self.associated_type = str @@ -388,11 +418,11 @@ def enforce_(self, value: object): class SeedInput(NumberInput): - def __init__(self, label: str = "Seed", has_handle: bool = True): + def __init__(self, label: str = "Seed", *, has_handle: bool = True): super().__init__( label=label, - minimum=None, - maximum=None, + min=None, + max=None, precision=0, default=0, label_style="default", @@ -425,6 +455,7 @@ class ColorInput(BaseInput[Color]): def __init__( self, label: str = "Color", + *, default: Color | None = None, channels: int | list[int] | None = None, ): @@ -529,7 +560,7 @@ def FillColor::getOutputChannels(fill: FillColor, channels: uint) { def TileSizeDropdown( - label: str = "Tile Size", estimate: bool = True, default: TileSize | None = None + label: str = "Tile Size", *, estimate: bool = True, default: TileSize | None = None ) -> DropDownInput: options = [] if estimate: @@ -555,3 +586,16 @@ def TileSizeDropdown( class AudioStreamInput(BaseInput): def __init__(self, label: str = "Audio Stream"): super().__init__("AudioStream", label, kind="generic") + + +class OrderEnum(Enum): + ROW_MAJOR = 0 + COLUMN_MAJOR = 1 + + +def RowOrderDropdown() -> DropDownInput: + return EnumInput( + OrderEnum, + label="Order", + default=OrderEnum.ROW_MAJOR, + ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/image_dropdown_inputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/image_dropdown_inputs.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/image_dropdown_inputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/image_dropdown_inputs.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/label.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/label.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/label.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/label.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/numeric_inputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numeric_inputs.py similarity index 70% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/numeric_inputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numeric_inputs.py index 02c2c41..cd795af 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/numeric_inputs.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numeric_inputs.py @@ -1,7 +1,7 @@ from __future__ import annotations import math -from typing import Literal +from typing import Literal, Union import navi from api import BaseInput, InputConversion, InputKind @@ -9,15 +9,28 @@ from ...utils.utils import round_half_up from .label import LabelStyle, get_default_label_style +Precision = Union[int, Literal["unlimited"]] + + +def _get_step(precision: Precision) -> float: + if precision == "unlimited": + return 1 + return 10**-precision + + +def _is_int(precision: Precision) -> bool: + return precision == 0 + def clamp_number( value: float | int, - precision: int, + precision: Precision, min_value: float | int | None, max_value: float | int | None, ) -> float | int: # Convert proper number type - value = round_half_up(value) if precision == 0 else round(value, precision) + if precision != "unlimited": + value = round_half_up(value) if precision == 0 else round(value, precision) # Clamp to max and min, correcting for max/min not aligning with offset + n * step if max_value is not None: @@ -26,7 +39,7 @@ def clamp_number( value = max(value, min_value) # guarantee integers - if precision <= 0: + if _is_int(precision): return int(value) else: return float(value) @@ -35,9 +48,9 @@ def clamp_number( def get_number_type( min_value: float | int | None, max_value: float | int | None, - precision: int, + precision: Precision, ) -> navi.ExpressionJson: - if precision > 0: + if not _is_int(precision): # step is not an integer return navi.interval(min_value, max_value) return navi.int_interval(min_value, max_value) @@ -49,11 +62,12 @@ class NumberInput(BaseInput): def __init__( self, label: str, - precision: int = 0, - controls_step: float | int | None = None, + *, + precision: Precision = 0, + step: float | int | None = None, default: float | int = 0, - minimum: float | int | None = 0, - maximum: float | int | None = None, + min: float | int | None = 0, + max: float | int | None = None, unit: str | None = None, note_expression: str | None = None, kind: InputKind = "number", @@ -62,38 +76,32 @@ def __init__( has_handle: bool = True, ): super().__init__("number", label, kind=kind, has_handle=has_handle) - self.precision = precision + self.precision: int | Literal["unlimited"] = precision # controls_step is for increment/decrement arrows. - self.controls_step: float | int = ( - controls_step if controls_step is not None else 10**-precision - ) + self.step: float | int = step if step is not None else _get_step(precision) self.default = default - self.minimum = minimum - self.maximum = maximum + self.min = min + self.max = max self.unit = unit self.note_expression = note_expression self.hide_trailing_zeros = hide_trailing_zeros self.label_style: LabelStyle = label_style or get_default_label_style(label) - self.associated_type = float if precision > 0 else int + self.associated_type = float if not _is_int(precision) else int - self.input_type = get_number_type( - self.minimum, - self.maximum, - self.precision, - ) + self.input_type = get_number_type(self.min, self.max, self.precision) if self.precision == 0: self.input_conversions = [InputConversion("number", "round(Input)")] def to_dict(self): return { **super().to_dict(), - "min": self.minimum, - "max": self.maximum, + "min": self.min, + "max": self.max, "noteExpression": self.note_expression, "def": self.default, - "precision": self.precision, - "controlsStep": self.controls_step, + "precision": 100 if self.precision == "unlimited" else self.precision, + "controlsStep": self.step, "unit": self.unit, "hideTrailingZeros": self.hide_trailing_zeros, "labelStyle": self.label_style, @@ -109,7 +117,7 @@ def enforce(self, value: object): if math.isnan(value): raise ValueError("NaN is not a valid number") - return clamp_number(value, self.precision, self.minimum, self.maximum) + return clamp_number(value, self.precision, self.min, self.max) class SliderInput(NumberInput): @@ -118,11 +126,12 @@ class SliderInput(NumberInput): def __init__( self, label: str, - precision: int = 0, - controls_step: float | int | None = None, + *, + precision: Precision = 0, + step: float | int | None = None, slider_step: float | int | None = None, - minimum: float | int = 0, - maximum: float | int = 100, + min: float | int = 0, + max: float | int = 100, default: float | int = 50, unit: str | None = None, note_expression: str | None = None, @@ -135,10 +144,10 @@ def __init__( super().__init__( label, precision=precision, - controls_step=controls_step, + step=step, default=default, - minimum=minimum, - maximum=maximum, + min=min, + max=max, unit=unit, note_expression=note_expression, kind="slider", @@ -149,7 +158,7 @@ def __init__( self.slider_step = ( slider_step if slider_step is not None - else (controls_step if controls_step is not None else 10**-precision) + else (step if step is not None else _get_step(precision)) ) self.gradient = gradient self.scale = scale diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/numpy_inputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numpy_inputs.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/numpy_inputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numpy_inputs.py index 96c112b..1dc9d32 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/numpy_inputs.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numpy_inputs.py @@ -26,6 +26,7 @@ class ImageInput(BaseInput): def __init__( self, label: str = "Image", + *, image_type: navi.ExpressionJson = "Image | Color", channels: int | list[int] | None = None, allow_colors: bool = False, diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/pytorch_inputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/pytorch_inputs.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/pytorch_inputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/pytorch_inputs.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/file_outputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/file_outputs.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/file_outputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/file_outputs.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/generic_outputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/generic_outputs.py similarity index 90% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/generic_outputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/generic_outputs.py index aade670..adaedbb 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/generic_outputs.py +++ b/MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/generic_outputs.py @@ -97,6 +97,20 @@ def enforce(self, value: object) -> Color: return value +class BoolOutput(BaseOutput): + def __init__( + self, + label: str = "Logical", + *, + output_type: navi.ExpressionJson = "bool", + ): + super().__init__( + output_type=navi.intersect_with_error("bool", output_type), + label=label, + kind="generic", + ) + + class AudioStreamOutput(BaseOutput): def __init__(self, label: str = "Audio Stream"): super().__init__( diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/numpy_outputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/numpy_outputs.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/numpy_outputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/numpy_outputs.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/pytorch_outputs.py b/MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/pytorch_outputs.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/pytorch_outputs.py rename to MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/pytorch_outputs.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/__init__.py b/MangaJaNaiConverterGui/backend/src/nodes/utils/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/__init__.py rename to MangaJaNaiConverterGui/backend/src/nodes/utils/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/format.py b/MangaJaNaiConverterGui/backend/src/nodes/utils/format.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/format.py rename to MangaJaNaiConverterGui/backend/src/nodes/utils/format.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/seed.py b/MangaJaNaiConverterGui/backend/src/nodes/utils/seed.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/seed.py rename to MangaJaNaiConverterGui/backend/src/nodes/utils/seed.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/utils.py b/MangaJaNaiConverterGui/backend/src/nodes/utils/utils.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/utils.py rename to MangaJaNaiConverterGui/backend/src/nodes/utils/utils.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/__init__.py b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/__init__.py similarity index 99% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/__init__.py rename to MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/__init__.py index aabb680..a44852a 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/__init__.py +++ b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/__init__.py @@ -98,7 +98,7 @@ def get_pytorch(): Dependency( display_name="Spandrel", pypi_name="spandrel", - version="0.3.3", + version="0.3.4", size_estimate=264 * KB, ), Dependency( diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/__init__.py b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/__init__.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/__init__.py rename to MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/__init__.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/io/load_model.py b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/io/load_model.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/io/load_model.py rename to MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/io/load_model.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py similarity index 92% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py rename to MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py index f1ee269..e4db77f 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py +++ b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py @@ -1,5 +1,7 @@ from __future__ import annotations +import weakref + import numpy as np import psutil import torch @@ -9,6 +11,7 @@ from api import KeyInfo, NodeContext, Progress from nodes.groups import Condition, if_enum_group, if_group from nodes.impl.pytorch.auto_split import pytorch_auto_split +from nodes.impl.pytorch.utils import safe_cuda_cache_empty from nodes.impl.upscale.auto_split_tiles import ( CUSTOM, NO_TILING, @@ -33,6 +36,8 @@ from ...settings import PyTorchSettings, get_settings from .. import processing_group +MODEL_BYTES_CACHE = weakref.WeakKeyDictionary() + def upscale( img: np.ndarray, @@ -54,15 +59,16 @@ def upscale( tile_size = NO_TILING def estimate(): - element_size = 2 if use_fp16 else 4 - model_bytes = sum( - p.numel() * element_size for p in model.model.parameters() - ) + model_bytes = MODEL_BYTES_CACHE.get(model) + if model_bytes is None: + model_bytes = sum(p.numel() * 4 for p in model.model.parameters()) + MODEL_BYTES_CACHE[model] = model_bytes if "cuda" in device.type: + if options.use_fp16: + model_bytes = model_bytes // 2 mem_info: tuple[int, int] = torch.cuda.mem_get_info(device) # type: ignore free, _total = mem_info - element_size = 2 if use_fp16 else 4 if options.budget_limit > 0: free = min(options.budget_limit * 1024**3, free) budget = int(free * 0.8) @@ -72,7 +78,7 @@ def estimate(): budget, model_bytes, img, - element_size, + 2 if use_fp16 else 4, ) ) elif device.type == "cpu": @@ -85,7 +91,7 @@ def estimate(): budget, model_bytes, img, - element_size, + 4, ) ) return MaxTileSize() @@ -144,7 +150,7 @@ def estimate(): ), if_group(Condition.bool(4, True))( NumberInput( - "Scale", default=4, minimum=1, maximum=32, label_style="hidden" + "Scale", default=4, min=1, max=32, label_style="hidden" ).with_id(5), ), ), @@ -174,13 +180,10 @@ def estimate(): if_enum_group(2, CUSTOM)( NumberInput( "Custom Tile Size", - minimum=1, - maximum=None, + min=1, + max=None, default=TILE_SIZE_256, - precision=0, - controls_step=1, unit="px", - has_handle=False, ).with_id(6), ), if_group( @@ -257,11 +260,13 @@ def upscale_image_node( use_custom_scale: bool, custom_scale: int, tile_size: TileSize, - custom_tile_size: int | None, + custom_tile_size: int, separate_alpha: bool, ) -> np.ndarray: exec_options = get_settings(context) + context.add_cleanup(safe_cuda_cache_empty) + in_nc = model.input_channels out_nc = model.output_channels scale = model.scale @@ -280,9 +285,7 @@ def inner_upscale(img: np.ndarray) -> np.ndarray: lambda i: upscale( i, model, - TileSize(custom_tile_size) - if tile_size == CUSTOM and custom_tile_size is not None - else tile_size, + TileSize(custom_tile_size) if tile_size == CUSTOM else tile_size, exec_options, context, ), diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/settings.py b/MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/settings.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/settings.py rename to MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/settings.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/progress_controller.py b/MangaJaNaiConverterGui/backend/src/progress_controller.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/progress_controller.py rename to MangaJaNaiConverterGui/backend/src/progress_controller.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/runmangajanaiconverterguiupscale.py b/MangaJaNaiConverterGui/backend/src/run_upscale.py similarity index 50% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/runmangajanaiconverterguiupscale.py rename to MangaJaNaiConverterGui/backend/src/run_upscale.py index ae64edc..5c90b57 100644 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/runmangajanaiconverterguiupscale.py +++ b/MangaJaNaiConverterGui/backend/src/run_upscale.py @@ -1,20 +1,24 @@ -import platform import sys import json import os +import platform from pathlib import Path import ctypes import io import cv2 +import pillow_avif # noqa: F401 from PIL import Image, ImageFilter, ImageCms import numpy as np import argparse import zipfile import rarfile import time +from typing import Callable from multiprocess import Queue, Process, set_start_method from chainner_ext import resize, ResizeFilter +sys.path.append(os.path.normpath(os.path.dirname(os.path.abspath(__file__)))) + from packages.chaiNNer_pytorch.pytorch.io.load_model import load_model_node from api import ( NodeContext, @@ -23,7 +27,9 @@ from progress_controller import ProgressController, ProgressToken from nodes.utils.utils import get_h_w_c from nodes.impl.image_utils import cv_save_image, to_uint8, to_uint16, normalize -from packages.chaiNNer_pytorch.pytorch.processing.upscale_image import upscale_image_node +from packages.chaiNNer_pytorch.pytorch.processing.upscale_image import ( + upscale_image_node, +) from nodes.impl.upscale.auto_split_tiles import ( ESTIMATE, NO_TILING, @@ -34,7 +40,7 @@ class _ExecutorNodeContext(NodeContext): def __init__( - self, progress: ProgressToken, settings: SettingsParser, storage_dir: Path + self, progress: ProgressToken, settings: SettingsParser, storage_dir: Path ) -> None: super().__init__() @@ -42,6 +48,8 @@ def __init__( self.__settings = settings self._storage_dir = storage_dir + self.cleanup_fns: set[Callable[[], None]] = set() + @property def aborted(self) -> bool: return self.progress.aborted @@ -67,13 +75,16 @@ def settings(self) -> SettingsParser: def storage_dir(self) -> Path: return self._storage_dir + def add_cleanup(self, fn: Callable[[], None]) -> None: + self.cleanup_fns.add(fn) + def get_tile_size(tile_size_str): - if tile_size_str == 'Auto (Estimate)': + if tile_size_str == "Auto (Estimate)": return ESTIMATE - elif tile_size_str == 'Maximum': + elif tile_size_str == "Maximum": return MAX_TILE_SIZE - elif tile_size_str == 'No Tiling': + elif tile_size_str == "No Tiling": return NO_TILING elif tile_size_str.isdecimal(): return TileSize(int(tile_size_str)) @@ -130,6 +141,7 @@ def image_resize(image, new_size, is_grayscale): def get_system_codepage(): return None if is_linux else ctypes.windll.kernel32.GetConsoleOutputCP() + def enhance_contrast(image): # print('1', image[199][501], np.min(image), np.max(image)) image_p = Image.fromarray(image).convert("L") @@ -184,10 +196,15 @@ def enhance_contrast(image): if continuous_count > 1: break - print(f"Auto adjusted levels: new black level = {new_black_level}; new white level = {new_white_level}", flush=True) + print( + f"Auto adjusted levels: new black level = {new_black_level}; new white level = {new_white_level}", + flush=True, + ) - image_array = np.array(image_p).astype('float32') - image_array = np.maximum(image_array - new_black_level, 0) / (new_white_level - new_black_level) + image_array = np.array(image_p).astype("float32") + image_array = np.maximum(image_array - new_black_level, 0) / ( + new_white_level - new_black_level + ) image_array = np.clip(image_array, 0, 1) # gray_image = @@ -195,7 +212,9 @@ def enhance_contrast(image): def _read_cv(img_stream): - return cv2.imdecode(np.frombuffer(img_stream.read(), dtype=np.uint8), cv2.IMREAD_COLOR) + return cv2.imdecode( + np.frombuffer(img_stream.read(), dtype=np.uint8), cv2.IMREAD_COLOR + ) def _read_cv_from_path(path): @@ -203,7 +222,7 @@ def _read_cv_from_path(path): try: img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR) except Exception as cv_err: - logger.warning(f"Error loading image, trying with imdecode: {cv_err}") + print(f"Error loading image, trying with imdecode: {cv_err}", flush=True) if img is None: try: @@ -237,7 +256,7 @@ def _read_image_from_path(path): def _read_pil(img_stream): - im = Image.open(img_stream, formats=['AVIF']) + im = Image.open(img_stream, formats=["AVIF"]) return _pil_to_cv2(im) @@ -296,7 +315,9 @@ def get_chain_for_image(image, target_scale, target_width, target_height, chains original_height, original_width, _ = get_h_w_c(image) if target_width != 0 and target_height != 0: - target_scale = min(target_height / original_height, target_width / original_width) + target_scale = min( + target_height / original_height, target_width / original_width + ) if target_height != 0: target_scale = target_height / original_height elif target_width != 0: @@ -305,16 +326,20 @@ def get_chain_for_image(image, target_scale, target_width, target_height, chains is_grayscale = cv_image_is_grayscale(image) for chain in chains: - if should_chain_activate_for_image(original_width, original_height, is_grayscale, target_scale, chain): + if should_chain_activate_for_image( + original_width, original_height, is_grayscale, target_scale, chain + ): print("Matched Chain:", chain) return chain, is_grayscale, original_width, original_height return None, None, None, None -def should_chain_activate_for_image(original_width, original_height, is_grayscale, target_scale, chain): - min_width, min_height = [int(x) for x in chain['MinResolution'].split('x')] - max_width, max_height = [int(x) for x in chain['MaxResolution'].split('x')] +def should_chain_activate_for_image( + original_width, original_height, is_grayscale, target_scale, chain +): + min_width, min_height = [int(x) for x in chain["MinResolution"].split("x")] + max_width, max_height = [int(x) for x in chain["MaxResolution"].split("x")] # resolution tests if min_width != 0 and min_width > original_width: @@ -327,15 +352,15 @@ def should_chain_activate_for_image(original_width, original_height, is_grayscal return False # color / grayscale tests - if is_grayscale and not chain['IsGrayscale']: + if is_grayscale and not chain["IsGrayscale"]: return False - if not is_grayscale and not chain['IsColor']: + if not is_grayscale and not chain["IsColor"]: return False # scale tests - if chain['MaxScaleFactor'] != 0 and target_scale > chain['MaxScaleFactor']: + if chain["MaxScaleFactor"] != 0 and target_scale > chain["MaxScaleFactor"]: return False - if chain['MinScaleFactor'] != 0 and target_scale < chain['MinScaleFactor']: + if chain["MinScaleFactor"] != 0 and target_scale < chain["MinScaleFactor"]: return False return True @@ -344,7 +369,9 @@ def should_chain_activate_for_image(original_width, original_height, is_grayscal def ai_upscale_image(image, model_tile_size, model): global context if model is not None: - return upscale_image_node(context, image, model, False, 0, model_tile_size, None, False) + return upscale_image_node( + context, image, model, False, 0, model_tile_size, None, False + ) return image @@ -353,8 +380,15 @@ def postprocess_image(image): return to_uint8(image, normalized=True) -def final_target_resize(image, target_scale, target_width, target_height, original_width, original_height, - is_grayscale): +def final_target_resize( + image, + target_scale, + target_width, + target_height, + original_width, + original_height, + is_grayscale, +): # fit to dimensions if target_height != 0 and target_width != 0: h, w = image.shape[:2] @@ -368,26 +402,46 @@ def final_target_resize(image, target_scale, target_width, target_height, origin if target_height != 0: h, w = image.shape[:2] if h != target_height: - return image_resize(image, (round(w * target_height / h), target_height), is_grayscale) + return image_resize( + image, (round(w * target_height / h), target_height), is_grayscale + ) # resize width, keep proportional height elif target_width != 0: h, w = image.shape[:2] if w != target_width: - return image_resize(image, (target_width, round(h * target_width / w)), is_grayscale) + return image_resize( + image, (target_width, round(h * target_width / w)), is_grayscale + ) else: h, w = image.shape[:2] new_target_height = original_height * target_scale if h != new_target_height: - return image_resize(image, (round(w * new_target_height / h), new_target_height), is_grayscale) + return image_resize( + image, + (round(w * new_target_height / h), new_target_height), + is_grayscale, + ) return image -def save_image_zip(image, file_name, output_zip, image_format, lossy_compression_quality, use_lossless_compression, - original_width, original_height, target_scale, target_width, target_height, is_grayscale): +def save_image_zip( + image, + file_name, + output_zip, + image_format, + lossy_compression_quality, + use_lossless_compression, + original_width, + original_height, + target_scale, + target_width, + target_height, + is_grayscale, +): print(f"save image to zip: {file_name}", flush=True) - if image_format == 'avif': + if image_format == "avif": image = to_uint8(image, normalized=True) channels = get_h_w_c(image)[2] if channels == 1: @@ -397,14 +451,21 @@ def save_image_zip(image, file_name, output_zip, image_format, lossy_compression elif channels == 4: image = cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA) - image = final_target_resize(image, target_scale, target_width, target_height, original_width, original_height, - is_grayscale) + image = final_target_resize( + image, + target_scale, + target_width, + target_height, + original_width, + original_height, + is_grayscale, + ) with Image.fromarray(image) as pil_im: output_buffer = io.BytesIO() pil_im.save(output_buffer, format=image_format) else: - if image_format == 'jpg': + if image_format == "jpg": params = [ cv2.IMWRITE_JPEG_QUALITY, int(lossy_compression_quality), @@ -414,7 +475,10 @@ def save_image_zip(image, file_name, output_zip, image_format, lossy_compression 1, # jpeg_progressive ] elif image_format == "webp": - params = [cv2.IMWRITE_WEBP_QUALITY, 101 if use_lossless_compression else int(lossy_compression_quality)] + params = [ + cv2.IMWRITE_WEBP_QUALITY, + 101 if use_lossless_compression else int(lossy_compression_quality), + ] else: params = [] @@ -429,8 +493,15 @@ def save_image_zip(image, file_name, output_zip, image_format, lossy_compression # chainner images are always f32 pass - image = final_target_resize(image, target_scale, target_width, target_height, original_width, original_height, - is_grayscale) + image = final_target_resize( + image, + target_scale, + target_width, + target_height, + original_width, + original_height, + is_grayscale, + ) # Convert the resized image back to bytes _, buf_img = cv2.imencode(f".{image_format}", image, params) @@ -442,12 +513,23 @@ def save_image_zip(image, file_name, output_zip, image_format, lossy_compression output_zip.writestr(file_name, upscaled_image_data) -def save_image(image, output_file_path, image_format, lossy_compression_quality, use_lossless_compression, - original_width, original_height, target_scale, target_width, target_height, is_grayscale): +def save_image( + image, + output_file_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + original_width, + original_height, + target_scale, + target_width, + target_height, + is_grayscale, +): print(f"save image: {output_file_path}", flush=True) # save with pillow - if image_format == 'avif': + if image_format == "avif": image = to_uint8(image, normalized=True) channels = get_h_w_c(image)[2] if channels == 1: @@ -457,14 +539,21 @@ def save_image(image, output_file_path, image_format, lossy_compression_quality, elif channels == 4: image = cv2.cvtColor(image, cv2.COLOR_BGRA2RGBA) - image = final_target_resize(image, target_scale, target_width, target_height, original_width, original_height, - is_grayscale) + image = final_target_resize( + image, + target_scale, + target_width, + target_height, + original_width, + original_height, + is_grayscale, + ) with Image.fromarray(image) as pil_im: pil_im.save(output_file_path, quality=lossy_compression_quality) else: # save with cv2 - if image_format == 'jpg': + if image_format == "jpg": params = [ cv2.IMWRITE_JPEG_QUALITY, int(lossy_compression_quality), @@ -474,7 +563,10 @@ def save_image(image, output_file_path, image_format, lossy_compression_quality, 1, # jpeg_progressive ] elif image_format == "webp": - params = [cv2.IMWRITE_WEBP_QUALITY, 101 if use_lossless_compression else int(lossy_compression_quality)] + params = [ + cv2.IMWRITE_WEBP_QUALITY, + 101 if use_lossless_compression else int(lossy_compression_quality), + ] else: params = [] @@ -489,31 +581,69 @@ def save_image(image, output_file_path, image_format, lossy_compression_quality, # chainner images are always f32 pass - image = final_target_resize(image, target_scale, target_width, target_height, original_width, original_height, - is_grayscale) + image = final_target_resize( + image, + target_scale, + target_width, + target_height, + original_width, + original_height, + is_grayscale, + ) cv_save_image(output_file_path, image, params) -def preprocess_worker_archive(upscale_queue, input_archive_path, output_archive_path, - target_scale, target_width, target_height, - chains, loaded_models): +def preprocess_worker_archive( + upscale_queue, + input_archive_path, + output_archive_path, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): """ given a zip or rar path, read images out of the archive, apply auto levels, add the image to upscale queue """ if input_archive_path.endswith(ZIP_EXTENSIONS): - with zipfile.ZipFile(input_archive_path, 'r') as input_zip: - preprocess_worker_archive_file(upscale_queue, input_zip, output_archive_path, target_scale, target_width, - target_height, chains, loaded_models) + with zipfile.ZipFile(input_archive_path, "r") as input_zip: + preprocess_worker_archive_file( + upscale_queue, + input_zip, + output_archive_path, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ) elif input_archive_path.endswith(RAR_EXTENSIONS): - with rarfile.RarFile(input_archive_path, 'r') as input_rar: - preprocess_worker_archive_file(upscale_queue, input_rar, output_archive_path, target_scale, target_width, - target_height, chains, loaded_models) - - -def preprocess_worker_archive_file(upscale_queue, input_archive, output_archive_path, target_scale, target_width, - target_height, chains, loaded_models): + with rarfile.RarFile(input_archive_path, "r") as input_rar: + preprocess_worker_archive_file( + upscale_queue, + input_rar, + output_archive_path, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ) + + +def preprocess_worker_archive_file( + upscale_queue, + input_archive, + output_archive_path, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): """ given an input zip or rar archive, read images out of the archive, apply auto levels, add the image to upscale queue """ @@ -523,8 +653,10 @@ def preprocess_worker_archive_file(upscale_queue, input_archive, output_archive_ for filename in namelist: decoded_filename = filename try: - decoded_filename = decoded_filename.encode('cp437').decode(f'cp{system_codepage}') - except: + decoded_filename = decoded_filename.encode("cp437").decode( + f"cp{system_codepage}" + ) + except: # noqa: E722 pass # Open the file inside the input zip @@ -537,47 +669,71 @@ def preprocess_worker_archive_file(upscale_queue, input_archive, output_archive_ image_bytes = io.BytesIO(image_data) image = _read_image(image_bytes, filename) - chain, is_grayscale, original_width, original_height = get_chain_for_image(image, target_scale, - target_width, - target_height, chains) + chain, is_grayscale, original_width, original_height = ( + get_chain_for_image( + image, target_scale, target_width, target_height, chains + ) + ) model = None if chain is not None: - resize_width_before_upscale = chain['ResizeWidthBeforeUpscale'] - resize_height_before_upscale = chain['ResizeHeightBeforeUpscale'] - resize_factor_before_upscale = chain['ResizeFactorBeforeUpscale'] + resize_width_before_upscale = chain["ResizeWidthBeforeUpscale"] + resize_height_before_upscale = chain["ResizeHeightBeforeUpscale"] + resize_factor_before_upscale = chain["ResizeFactorBeforeUpscale"] # resize width and height, distorting image - if resize_height_before_upscale != 0 and resize_width_before_upscale != 0: + if ( + resize_height_before_upscale != 0 + and resize_width_before_upscale != 0 + ): h, w = image.shape[:2] - image = standard_resize(image, (resize_width_before_upscale, resize_height_before_upscale)) + image = standard_resize( + image, + (resize_width_before_upscale, resize_height_before_upscale), + ) # resize height, keep proportional width elif resize_height_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, ( - round(w * resize_height_before_upscale / h), resize_height_before_upscale)) + image = standard_resize( + image, + ( + round(w * resize_height_before_upscale / h), + resize_height_before_upscale, + ), + ) # resize width, keep proportional height elif resize_width_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, ( - resize_width_before_upscale, round(h * resize_width_before_upscale / w))) + image = standard_resize( + image, + ( + resize_width_before_upscale, + round(h * resize_width_before_upscale / w), + ), + ) elif resize_factor_before_upscale != 100: h, w = image.shape[:2] - image = standard_resize(image, ( - round(w * resize_factor_before_upscale / 100), - round(h * resize_factor_before_upscale / 100))) + image = standard_resize( + image, + ( + round(w * resize_factor_before_upscale / 100), + round(h * resize_factor_before_upscale / 100), + ), + ) - if is_grayscale and chain['AutoAdjustLevels']: + if is_grayscale and chain["AutoAdjustLevels"]: image = enhance_contrast(image) else: image = normalize(image) - model_abs_path = get_model_abs_path(chain['ModelFilePath']) + model_abs_path = get_model_abs_path(chain["ModelFilePath"]) if model_abs_path in loaded_models: model = loaded_models[model_abs_path] elif os.path.exists(model_abs_path): - model, dirname, basename = load_model_node(context, Path(model_abs_path)) + model, dirname, basename = load_model_node( + context, Path(model_abs_path) + ) if model is not None: loaded_models[model_abs_path] = model else: @@ -585,89 +741,158 @@ def preprocess_worker_archive_file(upscale_queue, input_archive, output_archive_ image = np.ascontiguousarray(image) - upscale_queue.put((image, decoded_filename, True, is_grayscale, original_width, original_height, - get_tile_size(chain['ModelTileSize']), model)) + upscale_queue.put( + ( + image, + decoded_filename, + True, + is_grayscale, + original_width, + original_height, + get_tile_size(chain["ModelTileSize"]), + model, + ) + ) except Exception as e: - print(f"could not read as image, copying file to zip instead of upscaling: {decoded_filename}, {e}", - flush=True) - upscale_queue.put((image_data, decoded_filename, False, False, None, None, None, None)) + print( + f"could not read as image, copying file to zip instead of upscaling: {decoded_filename}, {e}", + flush=True, + ) + upscale_queue.put( + (image_data, decoded_filename, False, False, None, None, None, None) + ) # pass upscale_queue.put(UPSCALE_SENTINEL) # print("preprocess_worker_archive exiting") -def preprocess_worker_folder(upscale_queue, input_folder_path, output_folder_path, - output_filename, upscale_images, upscale_archives, overwrite_existing_files, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, chains, loaded_models): +def preprocess_worker_folder( + upscale_queue, + input_folder_path, + output_folder_path, + output_filename, + upscale_images, + upscale_archives, + overwrite_existing_files, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): """ given a folder path, recursively iterate the folder """ - print(f"preprocess_worker_folder entering {input_folder_path} {output_folder_path} {output_filename}", flush=True) + print( + f"preprocess_worker_folder entering {input_folder_path} {output_folder_path} {output_filename}", + flush=True, + ) for root, dirs, files in os.walk(input_folder_path): - for filename in files: # for output file, create dirs if necessary, or skip if file exists and overwrite not enabled input_file_base = Path(filename).stem - filename_rel = os.path.relpath(os.path.join(root, filename), input_folder_path) - output_filename_rel = os.path.join(os.path.dirname(filename_rel), - output_filename.replace('%filename%', input_file_base)) - output_file_path = Path(os.path.join(output_folder_path, output_filename_rel)) + filename_rel = os.path.relpath( + os.path.join(root, filename), input_folder_path + ) + output_filename_rel = os.path.join( + os.path.dirname(filename_rel), + output_filename.replace("%filename%", input_file_base), + ) + output_file_path = Path( + os.path.join(output_folder_path, output_filename_rel) + ) if filename.lower().endswith(IMAGE_EXTENSIONS): # TODO if image if upscale_images: - output_file_path = str(Path(f"{output_file_path}.{image_format}")).replace('%filename%', - input_file_base) + output_file_path = str( + Path(f"{output_file_path}.{image_format}") + ).replace("%filename%", input_file_base) - if not overwrite_existing_files and os.path.isfile(output_file_path): + if not overwrite_existing_files and os.path.isfile( + output_file_path + ): print(f"file exists, skip: {output_file_path}", flush=True) continue os.makedirs(os.path.dirname(output_file_path), exist_ok=True) image = _read_image_from_path(os.path.join(root, filename)) - chain, is_grayscale, original_width, original_height = get_chain_for_image(image, target_scale, - target_width, - target_height, chains) + chain, is_grayscale, original_width, original_height = ( + get_chain_for_image( + image, target_scale, target_width, target_height, chains + ) + ) model = None if chain is not None: - resize_width_before_upscale = chain['ResizeWidthBeforeUpscale'] - resize_height_before_upscale = chain['ResizeHeightBeforeUpscale'] - resize_factor_before_upscale = chain['ResizeFactorBeforeUpscale'] + resize_width_before_upscale = chain["ResizeWidthBeforeUpscale"] + resize_height_before_upscale = chain[ + "ResizeHeightBeforeUpscale" + ] + resize_factor_before_upscale = chain[ + "ResizeFactorBeforeUpscale" + ] # resize width and height, distorting image - if resize_height_before_upscale != 0 and resize_width_before_upscale != 0: + if ( + resize_height_before_upscale != 0 + and resize_width_before_upscale != 0 + ): h, w = image.shape[:2] - image = standard_resize(image, (resize_width_before_upscale, resize_height_before_upscale)) + image = standard_resize( + image, + ( + resize_width_before_upscale, + resize_height_before_upscale, + ), + ) # resize height, keep proportional width elif resize_height_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, ( - round(w * resize_height_before_upscale / h), resize_height_before_upscale)) + image = standard_resize( + image, + ( + round(w * resize_height_before_upscale / h), + resize_height_before_upscale, + ), + ) # resize width, keep proportional height elif resize_width_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, ( - resize_width_before_upscale, round(h * resize_width_before_upscale / w))) + image = standard_resize( + image, + ( + resize_width_before_upscale, + round(h * resize_width_before_upscale / w), + ), + ) elif resize_factor_before_upscale != 100: h, w = image.shape[:2] - image = standard_resize(image, ( - round(w * resize_factor_before_upscale / 100), - round(h * resize_factor_before_upscale / 100))) - - if is_grayscale and chain['AutoAdjustLevels']: + image = standard_resize( + image, + ( + round(w * resize_factor_before_upscale / 100), + round(h * resize_factor_before_upscale / 100), + ), + ) + + if is_grayscale and chain["AutoAdjustLevels"]: image = enhance_contrast(image) else: image = normalize(image) - model_abs_path = get_model_abs_path(chain['ModelFilePath']) + model_abs_path = get_model_abs_path(chain["ModelFilePath"]) if model_abs_path in loaded_models: model = loaded_models[model_abs_path] elif os.path.exists(model_abs_path): - model, dirname, basename = load_model_node(context, Path(model_abs_path)) + model, dirname, basename = load_model_node( + context, Path(model_abs_path) + ) if model is not None: loaded_models[model_abs_path] = model else: @@ -676,27 +901,54 @@ def preprocess_worker_folder(upscale_queue, input_folder_path, output_folder_pat image = np.ascontiguousarray(image) upscale_queue.put( - (image, output_filename_rel, True, is_grayscale, original_width, original_height, - get_tile_size(chain['ModelTileSize']), model)) + ( + image, + output_filename_rel, + True, + is_grayscale, + original_width, + original_height, + get_tile_size(chain["ModelTileSize"]), + model, + ) + ) elif filename.lower().endswith(ZIP_EXTENSIONS): # TODO if archive if upscale_archives: - output_file_path = str(output_file_path.with_suffix('.cbz')) - if not overwrite_existing_files and os.path.isfile(output_file_path): + output_file_path = str(output_file_path.with_suffix(".cbz")) + if not overwrite_existing_files and os.path.isfile( + output_file_path + ): print(f"file exists, skip: {output_file_path}", flush=True) continue os.makedirs(os.path.dirname(output_file_path), exist_ok=True) - upscale_archive_file(os.path.join(root, filename), output_file_path, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, chains, - loaded_models) # TODO custom output extension + upscale_archive_file( + os.path.join(root, filename), + output_file_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ) # TODO custom output extension upscale_queue.put(UPSCALE_SENTINEL) # print("preprocess_worker_folder exiting") -def preprocess_worker_image(upscale_queue, input_image_path, output_image_path, overwrite_existing_files, - target_scale, target_width, target_height, - chains, loaded_models): +def preprocess_worker_image( + upscale_queue, + input_image_path, + output_image_path, + overwrite_existing_files, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): """ given an image path, apply auto levels and add to upscale queue """ @@ -711,46 +963,69 @@ def preprocess_worker_image(upscale_queue, input_image_path, output_image_path, # with Image.open(input_image_path) as img: image = _read_image_from_path(input_image_path) - chain, is_grayscale, original_width, original_height = get_chain_for_image(image, target_scale, target_width, - target_height, - chains) + chain, is_grayscale, original_width, original_height = get_chain_for_image( + image, target_scale, target_width, target_height, chains + ) model = None if chain is not None: - resize_width_before_upscale = chain['ResizeWidthBeforeUpscale'] - resize_height_before_upscale = chain['ResizeHeightBeforeUpscale'] - resize_factor_before_upscale = chain['ResizeFactorBeforeUpscale'] + resize_width_before_upscale = chain["ResizeWidthBeforeUpscale"] + resize_height_before_upscale = chain["ResizeHeightBeforeUpscale"] + resize_factor_before_upscale = chain["ResizeFactorBeforeUpscale"] # resize width and height, distorting image if resize_height_before_upscale != 0 and resize_width_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, (resize_width_before_upscale, resize_height_before_upscale)) + image = standard_resize( + image, (resize_width_before_upscale, resize_height_before_upscale) + ) # resize height, keep proportional width elif resize_height_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, - (round(w * resize_height_before_upscale / h), resize_height_before_upscale)) + image = standard_resize( + image, + ( + round(w * resize_height_before_upscale / h), + resize_height_before_upscale, + ), + ) # resize width, keep proportional height elif resize_width_before_upscale != 0: h, w = image.shape[:2] - image = standard_resize(image, - (resize_width_before_upscale, round(h * resize_width_before_upscale / w))) + image = standard_resize( + image, + ( + resize_width_before_upscale, + round(h * resize_width_before_upscale / w), + ), + ) elif resize_factor_before_upscale != 100: h, w = image.shape[:2] - image = standard_resize(image, ( - round(w * resize_factor_before_upscale / 100), round(h * resize_factor_before_upscale / 100))) - - if is_grayscale and chain['AutoAdjustLevels']: + image = standard_resize( + image, + ( + round(w * resize_factor_before_upscale / 100), + round(h * resize_factor_before_upscale / 100), + ), + ) + + if is_grayscale and chain["AutoAdjustLevels"]: image = enhance_contrast(image) else: image = normalize(image) - model_abs_path = get_model_abs_path(chain['ModelFilePath']) + model_abs_path = get_model_abs_path(chain["ModelFilePath"]) + + if not os.path.exists(model_abs_path): + raise FileNotFoundError(model_abs_path) + print("model_abs_path", model_abs_path, os.path.exists(model_abs_path)) if model_abs_path in loaded_models: model = loaded_models[model_abs_path] elif os.path.exists(model_abs_path): - model, dirname, basename = load_model_node(context, Path(model_abs_path)) + model, dirname, basename = load_model_node( + context, Path(model_abs_path) + ) if model is not None: loaded_models[model_abs_path] = model else: @@ -760,8 +1035,17 @@ def preprocess_worker_image(upscale_queue, input_image_path, output_image_path, image = np.ascontiguousarray(image) upscale_queue.put( - (image, None, True, is_grayscale, original_width, original_height, get_tile_size(chain['ModelTileSize']), - model)) + ( + image, + None, + True, + is_grayscale, + original_width, + original_height, + get_tile_size(chain["ModelTileSize"]), + model, + ) + ) upscale_queue.put(UPSCALE_SENTINEL) @@ -771,98 +1055,205 @@ def upscale_worker(upscale_queue, postprocess_queue): """ # print("upscale_worker entering") while True: - image, file_name, is_image, is_grayscale, original_width, original_height, model_tile_size, model = upscale_queue.get() + ( + image, + file_name, + is_image, + is_grayscale, + original_width, + original_height, + model_tile_size, + model, + ) = upscale_queue.get() if image is None: break if is_image: image = ai_upscale_image(image, model_tile_size, model) - postprocess_queue.put((image, file_name, is_image, is_grayscale, original_width, original_height)) + postprocess_queue.put( + (image, file_name, is_image, is_grayscale, original_width, original_height) + ) postprocess_queue.put(POSTPROCESS_SENTINEL) # print("upscale_worker exiting") -def postprocess_worker_zip(postprocess_queue, output_zip_path, image_format, lossy_compression_quality, - use_lossless_compression, target_scale, target_width, target_height): +def postprocess_worker_zip( + postprocess_queue, + output_zip_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, +): """ wait for postprocess queue, for each queue entry, save the image to the zip file """ # print("postprocess_worker_zip entering") - with zipfile.ZipFile(output_zip_path, 'w') as output_zip: + with zipfile.ZipFile(output_zip_path, "w") as output_zip: while True: - image, file_name, is_image, is_grayscale, original_width, original_height = postprocess_queue.get() + ( + image, + file_name, + is_image, + is_grayscale, + original_width, + original_height, + ) = postprocess_queue.get() if image is None: break if is_image: # image = postprocess_image(image) - save_image_zip(image, str(Path(file_name).with_suffix(f'.{image_format}')), output_zip, image_format, - lossy_compression_quality, use_lossless_compression, original_width, original_height, - target_scale, target_width, target_height, is_grayscale) + save_image_zip( + image, + str(Path(file_name).with_suffix(f".{image_format}")), + output_zip, + image_format, + lossy_compression_quality, + use_lossless_compression, + original_width, + original_height, + target_scale, + target_width, + target_height, + is_grayscale, + ) else: # copy file output_zip.writestr(file_name, image) print("PROGRESS=postprocess_worker_zip_image", flush=True) print("PROGRESS=postprocess_worker_zip_archive", flush=True) -def postprocess_worker_folder(postprocess_queue, output_folder_path, image_format, lossy_compression_quality, - use_lossless_compression, target_scale, target_width, target_height): +def postprocess_worker_folder( + postprocess_queue, + output_folder_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, +): """ wait for postprocess queue, for each queue entry, save the image to the output folder """ # print("postprocess_worker_folder entering") while True: - image, file_name, _, is_grayscale, original_width, original_height = postprocess_queue.get() + image, file_name, _, is_grayscale, original_width, original_height = ( + postprocess_queue.get() + ) if image is None: break image = postprocess_image(image) - save_image(image, os.path.join(output_folder_path, str(Path(f"{file_name}.{image_format}"))), image_format, - lossy_compression_quality, use_lossless_compression, original_width, original_height, - target_scale, target_width, target_height, is_grayscale) + save_image( + image, + os.path.join(output_folder_path, str(Path(f"{file_name}.{image_format}"))), + image_format, + lossy_compression_quality, + use_lossless_compression, + original_width, + original_height, + target_scale, + target_width, + target_height, + is_grayscale, + ) print("PROGRESS=postprocess_worker_folder", flush=True) # print("postprocess_worker_folder exiting") -def postprocess_worker_image(postprocess_queue, output_file_path, image_format, lossy_compression_quality, - use_lossless_compression, target_scale, target_width, target_height): +def postprocess_worker_image( + postprocess_queue, + output_file_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, +): """ wait for postprocess queue, for each queue entry, save the image to the output file path """ while True: - image, _, _, is_grayscale, original_width, original_height = postprocess_queue.get() + image, _, _, is_grayscale, original_width, original_height = ( + postprocess_queue.get() + ) if image is None: break # image = postprocess_image(image) - save_image(image, output_file_path, image_format, lossy_compression_quality, use_lossless_compression, - original_width, original_height, target_scale, target_width, target_height, is_grayscale) + save_image( + image, + output_file_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + original_width, + original_height, + target_scale, + target_width, + target_height, + is_grayscale, + ) print("PROGRESS=postprocess_worker_image", flush=True) -def upscale_archive_file(input_zip_path, output_zip_path, image_format, - lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, - chains, loaded_models): +def upscale_archive_file( + input_zip_path, + output_zip_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): # TODO accept multiple paths to reuse simple queues? upscale_queue = Queue(maxsize=1) postprocess_queue = Queue(maxsize=1) # start preprocess zip process - preprocess_process = Process(target=preprocess_worker_archive, - args=(upscale_queue, input_zip_path, output_zip_path, - target_scale, target_width, target_height, - chains, loaded_models)) + preprocess_process = Process( + target=preprocess_worker_archive, + args=( + upscale_queue, + input_zip_path, + output_zip_path, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ), + ) preprocess_process.start() # start upscale process - upscale_process = Process(target=upscale_worker, args=(upscale_queue, postprocess_queue)) + upscale_process = Process( + target=upscale_worker, args=(upscale_queue, postprocess_queue) + ) upscale_process.start() # start postprocess zip process - postprocess_process = Process(target=postprocess_worker_zip, - args=(postprocess_queue, output_zip_path, image_format, lossy_compression_quality, - use_lossless_compression, target_scale, target_width, target_height)) + postprocess_process = Process( + target=postprocess_worker_zip, + args=( + postprocess_queue, + output_zip_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + ), + ) postprocess_process.start() # wait for all processes @@ -871,31 +1262,59 @@ def upscale_archive_file(input_zip_path, output_zip_path, image_format, postprocess_process.join() -def upscale_image_file(input_image_path, output_image_path, overwrite_existing_files, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, - chains, loaded_models): +def upscale_image_file( + input_image_path, + output_image_path, + overwrite_existing_files, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): upscale_queue = Queue(maxsize=1) postprocess_queue = Queue(maxsize=1) # start preprocess image process - preprocess_process = Process(target=preprocess_worker_image, args=(upscale_queue, input_image_path, - output_image_path, overwrite_existing_files, - target_scale, target_width, target_height, - chains, loaded_models)) + preprocess_process = Process( + target=preprocess_worker_image, + args=( + upscale_queue, + input_image_path, + output_image_path, + overwrite_existing_files, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ), + ) preprocess_process.start() # start upscale process - upscale_process = Process(target=upscale_worker, args=(upscale_queue, postprocess_queue)) + upscale_process = Process( + target=upscale_worker, args=(upscale_queue, postprocess_queue) + ) upscale_process.start() # start postprocess image process - postprocess_process = Process(target=postprocess_worker_image, args=(postprocess_queue, output_image_path, - image_format, lossy_compression_quality, - use_lossless_compression, - target_scale, - target_width, - target_height)) + postprocess_process = Process( + target=postprocess_worker_image, + args=( + postprocess_queue, + output_image_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + ), + ) postprocess_process.start() # wait for all processes @@ -904,44 +1323,92 @@ def upscale_image_file(input_image_path, output_image_path, overwrite_existing_f postprocess_process.join() -def upscale_file(input_file_path, output_folder_path, output_filename, overwrite_existing_files, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, - chains, loaded_models): +def upscale_file( + input_file_path, + output_folder_path, + output_filename, + overwrite_existing_files, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): input_file_base = Path(input_file_path).stem if input_file_path.lower().endswith(ARCHIVE_EXTENSIONS): - output_file_path = str( - Path(os.path.join(output_folder_path, output_filename.replace('%filename%', input_file_base))).with_suffix( - '.cbz')) + Path( + os.path.join( + output_folder_path, + output_filename.replace("%filename%", input_file_base), + ) + ).with_suffix(".cbz") + ) if not overwrite_existing_files and os.path.isfile(output_file_path): print(f"file exists, skip: {output_file_path}", flush=True) return - upscale_archive_file(input_file_path, output_file_path, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, - chains, loaded_models) + upscale_archive_file( + input_file_path, + output_file_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ) elif input_file_path.lower().endswith(IMAGE_EXTENSIONS): - output_file_path = str( - Path(os.path.join(output_folder_path, output_filename.replace('%filename%', input_file_base))).with_suffix( - f'.{image_format}')) + Path( + os.path.join( + output_folder_path, + output_filename.replace("%filename%", input_file_base), + ) + ).with_suffix(f".{image_format}") + ) if not overwrite_existing_files and os.path.isfile(output_file_path): print(f"file exists, skip: {output_file_path}", flush=True) return - upscale_image_file(input_file_path, output_file_path, overwrite_existing_files, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, - chains, loaded_models) + upscale_image_file( + input_file_path, + output_file_path, + overwrite_existing_files, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ) -def upscale_folder(input_folder_path, output_folder_path, output_filename, upscale_images, upscale_archives, - overwrite_existing_files, image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, chains, loaded_models): +def upscale_folder( + input_folder_path, + output_folder_path, + output_filename, + upscale_images, + upscale_archives, + overwrite_existing_files, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, +): # print("upscale_folder: entering") # preprocess_queue = Queue(maxsize=1) @@ -949,22 +1416,48 @@ def upscale_folder(input_folder_path, output_folder_path, output_filename, upsca postprocess_queue = Queue(maxsize=1) # start preprocess folder process - preprocess_process = Process(target=preprocess_worker_folder, - args=(upscale_queue, input_folder_path, output_folder_path, - output_filename, upscale_images, upscale_archives, overwrite_existing_files, - image_format, lossy_compression_quality, use_lossless_compression, - target_scale, target_width, target_height, chains, loaded_models)) + preprocess_process = Process( + target=preprocess_worker_folder, + args=( + upscale_queue, + input_folder_path, + output_folder_path, + output_filename, + upscale_images, + upscale_archives, + overwrite_existing_files, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + chains, + loaded_models, + ), + ) preprocess_process.start() # start upscale process - upscale_process = Process(target=upscale_worker, args=(upscale_queue, postprocess_queue)) + upscale_process = Process( + target=upscale_worker, args=(upscale_queue, postprocess_queue) + ) upscale_process.start() # start postprocess folder process - postprocess_process = Process(target=postprocess_worker_folder, args=(postprocess_queue, output_folder_path, - image_format, lossy_compression_quality, - use_lossless_compression, - target_scale, target_width, target_height)) + postprocess_process = Process( + target=postprocess_worker_folder, + args=( + postprocess_queue, + output_folder_path, + image_format, + lossy_compression_quality, + use_lossless_compression, + target_scale, + target_width, + target_height, + ), + ) postprocess_process.start() # wait for all processes @@ -972,64 +1465,78 @@ def upscale_folder(input_folder_path, output_folder_path, output_filename, upsca upscale_process.join() postprocess_process.join() + +current_file_directory = os.path.dirname(os.path.abspath(__file__)) + + def get_model_abs_path(chain_model_file_path): - relative_path = os.path.join("../../models", chain_model_file_path) if is_linux \ - else os.path.join("./models", chain_model_file_path) + return os.path.abspath(os.path.join(models_directory, chain_model_file_path)) - return os.path.abspath(relative_path) def get_gamma_icc_profile(): - profile_path = '../../ImageMagick/Custom Gray Gamma 1.0.icc' if is_linux else r'.\ImageMagick\Custom Gray Gamma 1.0.icc' + profile_path = os.path.join( + current_file_directory, "../ImageMagick/Custom Gray Gamma 1.0.icc" + ) return ImageCms.getOpenProfile(profile_path) + def get_dot20_icc_profile(): - profile_path = '../../ImageMagick/Dot Gain 20%.icc' if is_linux else r'.\ImageMagick\Dot Gain 20%.icc' + profile_path = os.path.join( + current_file_directory, "../ImageMagick/Dot Gain 20%.icc" + ) return ImageCms.getOpenProfile(profile_path) is_linux = platform.system() == "Linux" if is_linux: - set_start_method('spawn', force=True) + set_start_method("spawn", force=True) -sys.stdout.reconfigure(encoding='utf-8') +sys.stdout.reconfigure(encoding="utf-8") parser = argparse.ArgumentParser() -parser.add_argument('--settings', required=True) +parser.add_argument("--settings", required=True) args = parser.parse_args() with open(args.settings, mode="r", encoding="utf-8") as f: settings = json.load(f) -workflow = settings['Workflows']['$values'][settings['SelectedWorkflowIndex']] +workflow = settings["Workflows"]["$values"][settings["SelectedWorkflowIndex"]] +models_directory = settings["ModelsDirectory"] UPSCALE_SENTINEL = (None, None, None, None, None, None, None, None) POSTPROCESS_SENTINEL = (None, None, None, None, None, None) -CV2_IMAGE_EXTENSIONS = ('.png', '.jpg', '.jpeg', '.webp', '.bmp') -IMAGE_EXTENSIONS = CV2_IMAGE_EXTENSIONS + tuple('.avif') -ZIP_EXTENSIONS = ('.zip', '.cbz') -RAR_EXTENSIONS = ('.rar', '.cbr') +CV2_IMAGE_EXTENSIONS = (".png", ".jpg", ".jpeg", ".webp", ".bmp") +IMAGE_EXTENSIONS = CV2_IMAGE_EXTENSIONS + tuple(".avif") +ZIP_EXTENSIONS = (".zip", ".cbz") +RAR_EXTENSIONS = (".rar", ".cbr") ARCHIVE_EXTENSIONS = ZIP_EXTENSIONS + RAR_EXTENSIONS loaded_models = {} system_codepage = get_system_codepage() -settings = SettingsParser({ - 'use_cpu': settings['UseCpu'], - 'use_fp16': settings['UseFp16'], - 'gpu_index': settings['SelectedDeviceIndex'], - 'budget_limit': 0 -}) +settingsParser = SettingsParser( + { + "use_cpu": settings["UseCpu"], + "use_fp16": settings["UseFp16"], + "gpu_index": settings["SelectedDeviceIndex"], + "budget_limit": 0, + } +) -context = _ExecutorNodeContext(ProgressController(), settings, None) +context = _ExecutorNodeContext(ProgressController(), settingsParser, None) gamma1icc = get_gamma_icc_profile() dotgain20icc = get_dot20_icc_profile() -dotgain20togamma1transform = ImageCms.buildTransformFromOpenProfiles(dotgain20icc, gamma1icc, 'L', 'L') -gamma1todotgain20transform = ImageCms.buildTransformFromOpenProfiles(gamma1icc, dotgain20icc, 'L', 'L') +dotgain20togamma1transform = ImageCms.buildTransformFromOpenProfiles( + dotgain20icc, gamma1icc, "L", "L" +) +gamma1todotgain20transform = ImageCms.buildTransformFromOpenProfiles( + gamma1icc, dotgain20icc, "L", "L" +) -if __name__ == '__main__': +if __name__ == "__main__": # gc.disable() #TODO!!!!!!!!!!!! # Record the start time start_time = time.time() @@ -1048,31 +1555,48 @@ def get_dot20_icc_profile(): target_width = 0 target_height = 0 - if workflow['ModeScaleSelected']: - target_scale = workflow['UpscaleScaleFactor'] - elif workflow['ModeWidthSelected']: - target_width = workflow['ResizeWidthAfterUpscale'] - elif workflow['ModeHeightSelected']: - target_height = workflow['ResizeHeightAfterUpscale'] + if workflow["ModeScaleSelected"]: + target_scale = workflow["UpscaleScaleFactor"] + elif workflow["ModeWidthSelected"]: + target_width = workflow["ResizeWidthAfterUpscale"] + elif workflow["ModeHeightSelected"]: + target_height = workflow["ResizeHeightAfterUpscale"] else: - target_width = workflow['DisplayDeviceWidth'] - target_height = workflow['DisplayDeviceHeight'] - - if workflow['SelectedTabIndex'] == 1: - upscale_folder(workflow['InputFolderPath'], workflow['OutputFolderPath'], workflow['OutputFilename'], - workflow['UpscaleImages'], workflow['UpscaleArchives'], - workflow['OverwriteExistingFiles'], image_format, workflow['LossyCompressionQuality'], - workflow['UseLosslessCompression'], - target_scale, target_width, target_height, - workflow['Chains']['$values'], - loaded_models) - elif workflow['SelectedTabIndex'] == 0: - upscale_file(workflow['InputFilePath'], workflow['OutputFolderPath'], workflow['OutputFilename'], - workflow['OverwriteExistingFiles'], - image_format, workflow['LossyCompressionQuality'], workflow['UseLosslessCompression'], - target_scale, target_width, target_height, - workflow['Chains']['$values'], - loaded_models) + target_width = workflow["DisplayDeviceWidth"] + target_height = workflow["DisplayDeviceHeight"] + + if workflow["SelectedTabIndex"] == 1: + upscale_folder( + workflow["InputFolderPath"], + workflow["OutputFolderPath"], + workflow["OutputFilename"], + workflow["UpscaleImages"], + workflow["UpscaleArchives"], + workflow["OverwriteExistingFiles"], + image_format, + workflow["LossyCompressionQuality"], + workflow["UseLosslessCompression"], + target_scale, + target_width, + target_height, + workflow["Chains"]["$values"], + loaded_models, + ) + elif workflow["SelectedTabIndex"] == 0: + upscale_file( + workflow["InputFilePath"], + workflow["OutputFolderPath"], + workflow["OutputFilename"], + workflow["OverwriteExistingFiles"], + image_format, + workflow["LossyCompressionQuality"], + workflow["UseLosslessCompression"], + target_scale, + target_width, + target_height, + workflow["Chains"]["$values"], + loaded_models, + ) # # Record the end time end_time = time.time() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/system.py b/MangaJaNaiConverterGui/backend/src/system.py similarity index 100% rename from MangaJaNaiConverterGui/chaiNNer/backend/src/system.py rename to MangaJaNaiConverterGui/backend/src/system.py diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/.gitignore b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/inspectionProfiles/Project_Default.xml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index ce048c2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/inspectionProfiles/profiles_settings.xml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/misc.xml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/misc.xml deleted file mode 100644 index a6218fe..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/modules.xml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/modules.xml deleted file mode 100644 index f669a0e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/ruff.xml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/ruff.xml deleted file mode 100644 index 6002fb1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/ruff.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/src.iml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/src.iml deleted file mode 100644 index d5dcd1e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/src.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/vcs.xml b/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/vcs.xml deleted file mode 100644 index 4fce1d8..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/README.md b/MangaJaNaiConverterGui/chaiNNer/backend/src/README.md deleted file mode 100644 index fcc25ee..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Info -MangaJaNaiConverterGui is a convenient GUI windows tool, but in the backend it operates by running some python scripts in CLI. - -This README is for those interested in running only the CLI on a linux systems or WSL. - -# Setup -## Setup virtual environment and download dependencies -Navigate to `MangaJaNaiConverterGui/chaiNNer/backend/src/` and execute these commands: -```commandline -python -m venv mangajanai -source mangajanai/bin/activate -pip install -r linux-cli-requirements.txt -``` -This script will create a new python virtual environment, switch to it and download all required dependencies. - -By default, it uses pytorch for CUDA 12.1 compute platform. If you wish to change it, install proper version based on instruction from their [official website](https://pytorch.org/get-started/locally/). - -## Download models -Scripts expect all MangaJaNai models to be available in order to select the best one for each processed file. When running it as CLI on linux, put them in `MangaJaNaiConverterGui/chaiNNer/models` folder. - -You can extract them from the release .exe file of the version you wish to use. - - -# Usage -Script uses a settings file generated by the GUI to control its behavior. You need to build it manually to use the CLI. -The default settings can be found in [MangaJaNaiConverterGui/appstate2.json](MangaJaNaiConverterGui/appstate2.json). Just copy it and modify only what you need. -It's a long file, but you only need to worry about a few root keys and first workflow `Upscale Manga (Default)` - -// TODO - Add some convenient CLI params to control behavior without having to pass this big json. - -## Important Root Keys -- **SelectedDeviceIndex** - Controls which GPU should run upscaling jobs. If default doesn't work for you, check your devices by running [device_list.py](MangaJaNaiConverterGui/chaiNNer/backend/src/device_list.py) -- **UseCPU** - true/false -- **UseFp16** - true/false - -## Important Workflows Keys -- **SelectedTabIndex** - Choose if you want to upscale a single file or a whole folder - - 0 - file - - 1 - folder -- **InputFilePath** - absolute file path. Used when **SelectedTabIndex** = 0 -- **InputFolderPath** - absolute folder path. Used when **SelectedTabIndex** = 1 -- **OutputFilename** - Name of generated filenames. Keep `%filename%` to leave the same name -- **OutputFolderPath** - absolute folder path. -- **OverwriteExistingFiles** - true/false -- **UpscaleImages** - true/false - needs to be true for upscale to work -- **WebpSelected/AvifSelected/PngSelected/JpegSelected** - true/false. Only one should be true. Selects output filetype. -- **UpscaleScaleFactor** - 1/2/3/4 - How much you want to upscale which controls which models will be used -- There are a bunch of other options if you want to dig deeper into it, but setting these should be enough for basic usage - -## Execution -In `MangaJaNaiConverterGui/chaiNNer/backend/src/` directory run: -```bash -python runmangajanaiconverterguiupscale.py --settings "/path/to/your/file/appstate2.json" -``` diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/api.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/api.py deleted file mode 100644 index ffc7e02..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/api.py +++ /dev/null @@ -1,574 +0,0 @@ -from __future__ import annotations - -import importlib -import os -from dataclasses import dataclass, field -from typing import ( - Awaitable, - Callable, - Dict, - Generic, - Iterable, - List, - NewType, - Tuple, - TypedDict, - TypeVar, - Union, -) - -from sanic.log import logger - -from base_types import InputId, OutputId -from custom_types import NodeType, RunFn -from node_check import ( - NAME_CHECK_LEVEL, - TYPE_CHECK_LEVEL, - CheckFailedError, - CheckLevel, - check_naming_conventions, - check_schema_types, -) -from nodes.base_input import BaseInput -from nodes.base_output import BaseOutput -from nodes.group import Group, GroupId, NestedGroup, NestedIdGroup -from nodes.utils.exec_options import SettingsJson, get_execution_options - -KB = 1024**1 -MB = 1024**2 -GB = 1024**3 - - -def _process_inputs(base_inputs: Iterable[BaseInput | NestedGroup]): - inputs: List[BaseInput] = [] - groups: List[NestedIdGroup] = [] - - def add_inputs( - current: Iterable[BaseInput | NestedGroup], - ) -> List[InputId | NestedIdGroup]: - layout: List[InputId | NestedIdGroup] = [] - - for x in current: - if isinstance(x, Group): - if x.info.id == -1: - x.info.id = GroupId(len(groups)) - g: NestedIdGroup = Group(x.info, []) - groups.append(g) - layout.append(g) - g.items.extend(add_inputs(x.items)) # type: ignore - else: - if x.id == -1: - x.id = InputId(len(inputs)) - layout.append(x.id) - inputs.append(x) - - return layout - - return inputs, add_inputs(base_inputs) - - -def _process_outputs(base_outputs: Iterable[BaseOutput]): - outputs: List[BaseOutput] = [] - for i, output_value in enumerate(base_outputs): - if output_value.id == -1: - output_value.id = OutputId(i) - outputs.append(output_value) - return outputs - - -class DefaultNode(TypedDict): - schemaId: str - - -@dataclass(frozen=True) -class NodeData: - schema_id: str - description: str - see_also: List[str] - name: str - icon: str - type: NodeType - - inputs: List[BaseInput] - outputs: List[BaseOutput] - group_layout: List[InputId | NestedIdGroup] - - side_effects: bool - deprecated: bool - default_nodes: List[DefaultNode] | None # For iterators only - features: List[FeatureId] - - run: RunFn - - -T = TypeVar("T", bound=RunFn) -S = TypeVar("S") - - -@dataclass -class NodeGroup: - category: Category - name: str - nodes: List[NodeData] = field(default_factory=list) - - def add_node(self, node: NodeData): - logger.debug(f"Added {node.schema_id}") - self.nodes.append(node) - - def register( - self, - schema_id: str, - name: str, - description: str | List[str], - inputs: List[BaseInput | NestedGroup], - outputs: List[BaseOutput], - icon: str = "BsQuestionCircleFill", - node_type: NodeType = "regularNode", - side_effects: bool = False, - deprecated: bool = False, - default_nodes: List[DefaultNode] | None = None, - decorators: List[Callable] | None = None, - see_also: List[str] | str | None = None, - features: List[FeatureId] | FeatureId | None = None, - limited_to_8bpc: bool | str = False, - ): - if not isinstance(description, str): - description = "\n\n".join(description) - - if limited_to_8bpc: - description += "\n\n#### Limited color depth\n\n" - if isinstance(limited_to_8bpc, str): - description += f" {limited_to_8bpc}" - else: - description += ( - "This node will internally convert input images to 8 bits/channel." - " This is generally only a problem if you intend to save the output with 16 bits/channel or higher." - ) - - def to_list(x: List[S] | S | None) -> List[S]: - if x is None: - return [] - if isinstance(x, list): - return x - return [x] - - see_also = to_list(see_also) - features = to_list(features) - - def run_check(level: CheckLevel, run: Callable[[bool], None]): - if level == CheckLevel.NONE: - return - - try: - run(level == CheckLevel.FIX) - except CheckFailedError as e: - full_error_message = f"Error in {schema_id}: {e}" - if level == CheckLevel.ERROR: - # pylint: disable=raise-missing-from - raise CheckFailedError(full_error_message) - logger.warning(full_error_message) - - def inner_wrapper(wrapped_func: T) -> T: - p_inputs, group_layout = _process_inputs(inputs) - p_outputs = _process_outputs(outputs) - - run_check( - TYPE_CHECK_LEVEL, - lambda _: check_schema_types( - wrapped_func, node_type, p_inputs, p_outputs - ), - ) - run_check( - NAME_CHECK_LEVEL, - lambda fix: check_naming_conventions( - wrapped_func, node_type, name, fix - ), - ) - - if decorators is not None: - for decorator in decorators: - wrapped_func = decorator(wrapped_func) - - node = NodeData( - schema_id=schema_id, - name=name, - description=description, - see_also=see_also, - icon=icon, - type=node_type, - inputs=p_inputs, - group_layout=group_layout, - outputs=p_outputs, - side_effects=side_effects, - deprecated=deprecated, - default_nodes=default_nodes, - features=features, - run=wrapped_func, - ) - - self.add_node(node) - return wrapped_func - - return inner_wrapper - - -@dataclass -class Category: - package: Package - name: str - description: str - icon: str = "BsQuestionCircleFill" - color: str = "#777777" - install_hint: str | None = None - node_groups: List["NodeGroup"] = field(default_factory=list) - - def add_node_group(self, name: str) -> "NodeGroup": - result = NodeGroup(category=self, name=name) - self.node_groups.append(result) - return result - - def toDict(self): - return { - "name": self.name, - "description": self.description, - "icon": self.icon, - "color": self.color, - "installHint": self.install_hint, - } - - -@dataclass -class Dependency: - display_name: str - pypi_name: str - version: str - size_estimate: int | float - auto_update: bool = False - extra_index_url: str | None = None - - import_name: str | None = None - - def toDict(self): - return { - "displayName": self.display_name, - "pypiName": self.pypi_name, - "version": self.version, - "sizeEstimate": int(self.size_estimate), - "autoUpdate": self.auto_update, - "findLink": self.extra_index_url, - } - - -FeatureId = NewType("FeatureId", str) - - -@dataclass -class Feature: - id: str - name: str - description: str - behavior: FeatureBehavior | None = None - - def add_behavior(self, check: Callable[[], Awaitable[FeatureState]]) -> FeatureId: - if self.behavior is not None: - raise ValueError("Behavior already set") - - self.behavior = FeatureBehavior(check=check) - return FeatureId(self.id) - - def toDict(self): - return { - "id": self.id, - "name": self.name, - "description": self.description, - } - - -@dataclass -class FeatureBehavior: - check: Callable[[], Awaitable[FeatureState]] - - -@dataclass(frozen=True) -class FeatureState: - is_enabled: bool - details: str | None = None - - @staticmethod - def enabled(details: str | None = None) -> "FeatureState": - return FeatureState(is_enabled=True, details=details) - - @staticmethod - def disabled(details: str | None = None) -> "FeatureState": - return FeatureState(is_enabled=False, details=details) - - -@dataclass -class ToggleSetting: - label: str - key: str - description: str - default: bool = False - disabled: bool = False - type: str = "toggle" - - -class DropdownOption(TypedDict): - label: str - value: str - - -@dataclass -class DropdownSetting: - label: str - key: str - description: str - options: List[DropdownOption] - default: str - disabled: bool = False - type: str = "dropdown" - - -@dataclass -class NumberSetting: - label: str - key: str - description: str - min: float - max: float - default: float = 0 - disabled: bool = False - type: str = "number" - - -@dataclass -class CacheSetting: - label: str - key: str - description: str - directory: str - default: str = "" - disabled: bool = False - type: str = "cache" - - -Setting = Union[ToggleSetting, DropdownSetting, NumberSetting, CacheSetting] - - -class SettingsParser: - def __init__(self, raw: SettingsJson) -> None: - self.__settings = raw - - def get_bool(self, key: str, default: bool) -> bool: - value = self.__settings.get(key, default) - if isinstance(value, bool): - return value - raise ValueError(f"Invalid bool value for {key}: {value}") - - def get_int(self, key: str, default: int, parse_str: bool = False) -> int: - value = self.__settings.get(key, default) - if parse_str and isinstance(value, str): - return int(value) - if isinstance(value, int) and not isinstance(value, bool): - return value - raise ValueError(f"Invalid str value for {key}: {value}") - - def get_str(self, key: str, default: str) -> str: - value = self.__settings.get(key, default) - if isinstance(value, str): - return value - raise ValueError(f"Invalid str value for {key}: {value}") - - def get_cache_location(self, key: str) -> str | None: - value = self.__settings.get(key) - if isinstance(value, str) or value is None: - return value or None - raise ValueError(f"Invalid cache location value for {key}: {value}") - - -@dataclass -class Package: - where: str - id: str - name: str - description: str - icon: str - color: str - dependencies: List[Dependency] = field(default_factory=list) - categories: List[Category] = field(default_factory=list) - features: List[Feature] = field(default_factory=list) - settings: List[Setting] = field(default_factory=list) - - def add_category( - self, - name: str, - description: str, - icon: str, - color: str, - install_hint: str | None = None, - ) -> "Category": - result = Category( - package=self, - name=name, - description=description, - icon=icon, - color=color, - install_hint=install_hint, - ) - self.categories.append(result) - return result - - def add_dependency(self, dependency: Dependency): - self.dependencies.append(dependency) - - def add_setting(self, setting: Setting): - self.settings.append(setting) - - def add_feature( - self, - id: str, # pylint: disable=redefined-builtin - name: str, - description: str, - ) -> Feature: - if any(f.id == id for f in self.features): - raise ValueError(f"Duplicate feature id: {id}") - - feature = Feature(id=id, name=name, description=description) - self.features.append(feature) - return feature - - def get_settings(self) -> SettingsParser: - return SettingsParser(get_execution_options().get_package_settings(self.id)) - - -def _iter_py_files(directory: str): - for root, _, files in os.walk(directory): - for file in files: - if file.endswith(".py"): - yield os.path.join(root, file) - - -@dataclass -class LoadErrorInfo: - module: str - file: str - error: Exception - - -class PackageRegistry: - def __init__(self) -> None: - self.packages: Dict[str, Package] = {} - self.categories: List[Category] = [] - self.nodes: Dict[str, Tuple[NodeData, NodeGroup]] = {} - - def get_node(self, schema_id: str) -> NodeData: - return self.nodes[schema_id][0] - - def add(self, package: Package) -> Package: - # assert package.where not in self.packages - self.packages[package.where] = package - return package - - def load_nodes(self, current_file: str) -> List[LoadErrorInfo]: - load_error: List[LoadErrorInfo] = [] - failed_checks: List[CheckFailedError] = [] - - for package in list(self.packages.values()): - for file_path in _iter_py_files(os.path.dirname(package.where)): - _, name = os.path.split(file_path) - - if not name.startswith("_"): - module = os.path.relpath(file_path, os.path.dirname(current_file)) - module = module.replace("/", ".").replace("\\", ".")[: -len(".py")] - try: - importlib.import_module(module, package=None) - except CheckFailedError as e: - logger.error(e) - failed_checks.append(e) - except Exception as e: - load_error.append(LoadErrorInfo(module, file_path, e)) - - if len(failed_checks) > 0: - raise RuntimeError(f"Checks failed in {len(failed_checks)} node(s)") - - self._refresh_nodes() - - return load_error - - def _refresh_nodes(self): - self.nodes = {} - self.categories = [] - - for package in self.packages.values(): - self.categories.extend(package.categories) - for category in package.categories: - for sub in category.node_groups: - for node in sub.nodes: - if node.schema_id in self.nodes: - # print warning - pass - self.nodes[node.schema_id] = node, sub - - -registry = PackageRegistry() - - -def add_package( - where: str, - id: str, # pylint: disable=redefined-builtin - name: str, - description: str, - dependencies: List[Dependency] | None = None, - icon: str = "BsQuestionCircleFill", - color: str = "#777777", -) -> Package: - return registry.add( - Package( - where=where, - id=id, - name=name, - description=description, - icon=icon, - color=color, - dependencies=dependencies or [], - ) - ) - - -I = TypeVar("I") -L = TypeVar("L") - - -@dataclass -class Iterator(Generic[I]): - iter_supplier: Callable[[], Iterable[I]] - expected_length: int - - @staticmethod - def from_iter( - iter_supplier: Callable[[], Iterable[I]], expected_length: int - ) -> "Iterator[I]": - return Iterator(iter_supplier, expected_length) - - @staticmethod - def from_list(l: List[L], map_fn: Callable[[L, int], I]) -> "Iterator[I]": - """ - Creates a new iterator from a list that is mapped using the given - function. The iterable will be equivalent to `map(map_fn, l)`. - """ - - def supplier(): - for i, x in enumerate(l): - yield map_fn(x, i) - - return Iterator(supplier, len(l)) - - -N = TypeVar("N") -R = TypeVar("R") - - -@dataclass -class Collector(Generic[N, R]): - on_iterate: Callable[[N], None] - on_complete: Callable[[], R] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/base_types.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/base_types.py deleted file mode 100644 index 3237a3e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/base_types.py +++ /dev/null @@ -1,5 +0,0 @@ -from typing import NewType - -NodeId = NewType("NodeId", str) -InputId = NewType("InputId", int) -OutputId = NewType("OutputId", int) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/cache.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/cache.py deleted file mode 100644 index e809e03..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/cache.py +++ /dev/null @@ -1,152 +0,0 @@ -from __future__ import annotations - -import gc -from typing import Generic, Iterable, TypeVar - -from sanic.log import logger - -from api import NodeId - -from .chain import Chain, Edge, FunctionNode, NewIteratorNode - - -class CacheStrategy: - STATIC_HITS_TO_LIVE = 1_000_000_000 - - def __init__(self, hits_to_live: int) -> None: - assert hits_to_live >= 0 - self.hits_to_live = hits_to_live - - @property - def static(self) -> bool: - return self.hits_to_live == CacheStrategy.STATIC_HITS_TO_LIVE - - @property - def no_caching(self) -> bool: - return self.hits_to_live == 0 - - -StaticCaching = CacheStrategy(CacheStrategy.STATIC_HITS_TO_LIVE) -"""The value is cached for the during of the execution of the chain.""" - - -def get_cache_strategies(chain: Chain) -> dict[NodeId, CacheStrategy]: - """Create a map with the cache strategies for all nodes in the given chain.""" - - iterator_map = chain.get_parent_iterator_map() - - def any_are_iterated(out_edges: list[Edge]) -> bool: - for out_edge in out_edges: - target = chain.nodes[out_edge.target.id] - if isinstance(target, FunctionNode) and iterator_map[target] is not None: - return True - return False - - result: dict[NodeId, CacheStrategy] = {} - - for node in chain.nodes.values(): - strategy: CacheStrategy - - out_edges = chain.edges_from(node.id) - if isinstance(node, FunctionNode) and iterator_map[node] is not None: - # the function node is iterated - strategy = CacheStrategy(len(out_edges)) - else: - # the node is NOT implicitly iterated - - if isinstance(node, NewIteratorNode): - # we only care about non-iterator outputs - iterator_output = node.data.single_iterator_output - out_edges = [ - out_edge - for out_edge in out_edges - if out_edge.source.output_id not in iterator_output.outputs - ] - - if any_are_iterated(out_edges): - # some output is used by an iterated node - strategy = StaticCaching - else: - strategy = CacheStrategy(len(out_edges)) - - result[node.id] = strategy - - return result - - -T = TypeVar("T") - - -class _CacheEntry(Generic[T]): - def __init__(self, value: T, hits_to_live: int): - assert hits_to_live > 0 - self.value: T = value - self.hits_to_live: int = hits_to_live - - -class OutputCache(Generic[T]): - def __init__( - self, - parent: OutputCache[T] | None = None, - static_data: dict[NodeId, T] | None = None, - ): - super().__init__() - self.__static: dict[NodeId, T] = static_data.copy() if static_data else {} - self.__counted: dict[NodeId, _CacheEntry[T]] = {} - self.parent: OutputCache[T] | None = parent - - def keys(self) -> set[NodeId]: - keys: set[NodeId] = set() - keys.update(self.__static.keys(), self.__counted.keys()) - if self.parent: - keys.update(self.parent.keys()) - return keys - - def has(self, node_id: NodeId) -> bool: - if node_id in self.__static or node_id in self.__counted: - return True - if self.parent: - return self.parent.has(node_id) - return False - - def get(self, node_id: NodeId) -> T | None: - static_value = self.__static.get(node_id, None) - if static_value is not None: - return static_value - - counted = self.__counted.get(node_id, None) - if counted is not None: - value = counted.value - counted.hits_to_live -= 0 - if counted.hits_to_live <= 0: - logger.debug(f"Hits to live reached 0 for {node_id}") - del self.__counted[node_id] - gc.collect() - return value - - if self.parent is not None: - return self.parent.get(node_id) - - return None - - def set(self, node_id: NodeId, value: T, strategy: CacheStrategy): - if strategy.no_caching: - return - elif strategy.static: - self.__static[node_id] = value - else: - self.__counted[node_id] = _CacheEntry(value, strategy.hits_to_live) - - def delete(self, node_id: NodeId): - if node_id in self.__static: - del self.__static[node_id] - if node_id in self.__counted: - del self.__counted[node_id] - - def delete_many(self, node_ids: Iterable[NodeId]): - for node_id in node_ids: - self.delete(node_id) - - def clear(self): - self.__static.clear() - self.__counted.clear() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/chain.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/chain.py deleted file mode 100644 index cefa9e4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/chain.py +++ /dev/null @@ -1,202 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Callable, TypeVar, Union - -from api import InputId, NodeData, NodeId, OutputId, registry - -K = TypeVar("K") -V = TypeVar("V") - - -def get_or_add(d: dict[K, V], key: K, supplier: Callable[[], V]) -> V: - value = d.get(key) - if value is None: - value = supplier() - d[key] = value - return value - - -class FunctionNode: - def __init__(self, node_id: NodeId, schema_id: str): - self.id: NodeId = node_id - self.schema_id: str = schema_id - self.data: NodeData = registry.get_node(schema_id) - assert self.data.kind == "regularNode" - - def has_side_effects(self) -> bool: - return self.data.side_effects - - -class NewIteratorNode: - def __init__(self, node_id: NodeId, schema_id: str): - self.id: NodeId = node_id - self.schema_id: str = schema_id - self.data: NodeData = registry.get_node(schema_id) - assert self.data.kind == "newIterator" - - def has_side_effects(self) -> bool: - return self.data.side_effects - - -class CollectorNode: - def __init__(self, node_id: NodeId, schema_id: str): - self.id: NodeId = node_id - self.schema_id: str = schema_id - self.data: NodeData = registry.get_node(schema_id) - assert self.data.kind == "collector" - - def has_side_effects(self) -> bool: - return self.data.side_effects - - -Node = Union[FunctionNode, NewIteratorNode, CollectorNode] - - -@dataclass(frozen=True) -class EdgeSource: - id: NodeId - output_id: OutputId - - -@dataclass(frozen=True) -class EdgeTarget: - id: NodeId - input_id: InputId - - -@dataclass(frozen=True) -class Edge: - source: EdgeSource - target: EdgeTarget - - -class ChainInputs: - def __init__(self) -> None: - self.inputs: dict[NodeId, dict[InputId, object]] = {} - - def get(self, node_id: NodeId, input_id: InputId) -> object | None: - node = self.inputs.get(node_id) - if node is None: - return None - return node.get(input_id) - - def set(self, node_id: NodeId, input_id: InputId, value: object) -> None: - get_or_add(self.inputs, node_id, dict)[input_id] = value - - -class Chain: - def __init__(self): - self.nodes: dict[NodeId, Node] = {} - self.inputs: ChainInputs = ChainInputs() - self.__edges_by_source: dict[NodeId, list[Edge]] = {} - self.__edges_by_target: dict[NodeId, list[Edge]] = {} - - def add_node(self, node: Node): - assert node.id not in self.nodes, f"Duplicate node id {node.id}" - self.nodes[node.id] = node - - def add_edge(self, edge: Edge): - get_or_add(self.__edges_by_source, edge.source.id, list).append(edge) - get_or_add(self.__edges_by_target, edge.target.id, list).append(edge) - - def edges_from(self, source: NodeId) -> list[Edge]: - return self.__edges_by_source.get(source, []) - - def edges_to(self, target: NodeId) -> list[Edge]: - return self.__edges_by_target.get(target, []) - - def edge_to(self, target: NodeId, input_id: InputId) -> Edge | None: - """ - Returns the edge connected to the given input (if any). - """ - edges = self.__edges_by_target.get(target) - if edges is not None: - for e in edges: - if e.target.input_id == input_id: - return e - return None - - def remove_node(self, node_id: NodeId): - """ - Removes the node with the given id. - If the node is an iterator node, then all of its children will also be removed. - """ - - self.inputs.inputs.pop(node_id, None) - node = self.nodes.pop(node_id, None) - if node is None: - return - - for e in self.__edges_by_source.pop(node_id, []): - self.__edges_by_target[e.target.id].remove(e) - for e in self.__edges_by_target.pop(node_id, []): - self.__edges_by_source[e.source.id].remove(e) - - def remove_edge(self, edge: Edge) -> None: - """ - Removes the edge connected to the given input (if any). - """ - edges_target = self.__edges_by_target.get(edge.target.id) - if edges_target is not None: - edges_target.remove(edge) - edges_source = self.__edges_by_source.get(edge.source.id) - if edges_source is not None: - edges_source.remove(edge) - - def topological_order(self) -> list[NodeId]: - """ - Returns all nodes in topological order. - """ - result: list[NodeId] = [] - visited: set[NodeId] = set() - - def visit(node_id: NodeId): - if node_id in visited: - return - visited.add(node_id) - - for e in self.edges_from(node_id): - visit(e.target.id) - - result.append(node_id) - - for node_id in self.nodes: - visit(node_id) - - return result - - def get_parent_iterator_map(self) -> dict[FunctionNode, NewIteratorNode | None]: - """ - Returns a map of all function nodes to their parent iterator node (if any). - """ - iterator_cache: dict[FunctionNode, NewIteratorNode | None] = {} - - def get_iterator(r: FunctionNode) -> NewIteratorNode | None: - if r in iterator_cache: - return iterator_cache[r] - - iterator: NewIteratorNode | None = None - - for in_edge in self.edges_to(r.id): - source = self.nodes[in_edge.source.id] - if isinstance(source, FunctionNode): - iterator = get_iterator(source) - if iterator is not None: - break - elif isinstance(source, NewIteratorNode): - if ( - in_edge.source.output_id - in source.data.single_iterator_output.outputs - ): - iterator = source - break - - iterator_cache[r] = iterator - return iterator - - for node in self.nodes.values(): - if isinstance(node, FunctionNode): - get_iterator(node) - - return iterator_cache diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/input.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/input.py deleted file mode 100644 index db555f3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/input.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Union - -from api import NodeData, NodeId, OutputId - -from .chain import Chain - - -@dataclass(frozen=True) -class EdgeInput: - id: NodeId - index: int - - -@dataclass(frozen=True) -class ValueInput: - value: object - - -Input = Union[EdgeInput, ValueInput] - - -class InputMap: - def __init__(self) -> None: - self.data: dict[NodeId, list[Input]] = {} - - @staticmethod - def from_chain(chain: Chain) -> InputMap: - input_map = InputMap() - - def get_output_index(data: NodeData, output_id: OutputId) -> int: - for i, output in enumerate(data.outputs): - if output.id == output_id: - return i - raise AssertionError(f"Unknown output id {output_id}") - - for node in chain.nodes.values(): - inputs: list[Input] = [] - - for i in node.data.inputs: - edge = chain.edge_to(node.id, i.id) - if edge is not None: - source = chain.nodes[edge.source.id] - output_index = get_output_index(source.data, edge.source.output_id) - inputs.append(EdgeInput(edge.source.id, output_index)) - else: - inputs.append(ValueInput(chain.inputs.get(node.id, i.id))) - - input_map.data[node.id] = inputs - - return input_map - - def get(self, node_id: NodeId) -> list[Input]: - values = self.data.get(node_id, None) - if values is not None: - return values - - raise AssertionError(f"Unknown node id {node_id}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/json.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/json.py deleted file mode 100644 index def2668..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/json.py +++ /dev/null @@ -1,88 +0,0 @@ -from __future__ import annotations - -from typing import Literal, TypedDict, Union - -from api import NodeId - -from .chain import ( - Chain, - CollectorNode, - Edge, - EdgeSource, - EdgeTarget, - FunctionNode, - NewIteratorNode, -) - - -class JsonEdgeInput(TypedDict): - type: Literal["edge"] - id: NodeId - index: int - - -class JsonValueInput(TypedDict): - type: Literal["value"] - value: object - - -JsonInput = Union[JsonEdgeInput, JsonValueInput] - - -class JsonNode(TypedDict): - id: NodeId - schemaId: str - inputs: list[JsonInput] - parent: NodeId | None - nodeType: str - - -class IndexEdge: - def __init__( - self, from_id: NodeId, from_index: int, to_id: NodeId, to_index: int - ) -> None: - self.from_id = from_id - self.from_index = from_index - self.to_id = to_id - self.to_index = to_index - - -def parse_json(json: list[JsonNode]) -> Chain: - chain = Chain() - - index_edges: list[IndexEdge] = [] - - for json_node in json: - if json_node["nodeType"] == "newIterator": - node = NewIteratorNode(json_node["id"], json_node["schemaId"]) - elif json_node["nodeType"] == "collector": - node = CollectorNode(json_node["id"], json_node["schemaId"]) - else: - node = FunctionNode(json_node["id"], json_node["schemaId"]) - chain.add_node(node) - - inputs = node.data.inputs - for index, i in enumerate(json_node["inputs"]): - if i["type"] == "edge": - index_edges.append(IndexEdge(i["id"], i["index"], node.id, index)) - else: - chain.inputs.set(node.id, inputs[index].id, i["value"]) - - for index_edge in index_edges: - source_node = chain.nodes[index_edge.from_id].data - target_node = chain.nodes[index_edge.to_id].data - - chain.add_edge( - Edge( - EdgeSource( - index_edge.from_id, - source_node.outputs[index_edge.from_index].id, - ), - EdgeTarget( - index_edge.to_id, - target_node.inputs[index_edge.to_index].id, - ), - ) - ) - - return chain diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/optimize.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/optimize.py deleted file mode 100644 index 6c2e614..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/chain/optimize.py +++ /dev/null @@ -1,56 +0,0 @@ -from sanic.log import logger - -from .chain import Chain - - -class _Mutation: - def __init__(self) -> None: - self.changed = False - - def signal(self) -> None: - self.changed = True - - -def __removed_dead_nodes(chain: Chain, mutation: _Mutation): - """ - If a node does not have side effects and has no downstream nodes, then it can be removed. - """ - - for node in list(chain.nodes.values()): - is_dead = len(chain.edges_from(node.id)) == 0 and not node.has_side_effects() - if is_dead: - chain.remove_node(node.id) - mutation.signal() - logger.debug(f"Chain optimization: Removed {node.schema_id} node {node.id}") - - -def __static_switch_trim(chain: Chain, mutation: _Mutation): - """ - If the selected variant of the Switch node is statically known, then we can remove the input edges of all other variants. - """ - - for node in list(chain.nodes.values()): - if node.schema_id == "chainner:utility:switch": - value_index = chain.inputs.get(node.id, node.data.inputs[0].id) - if isinstance(value_index, int): - for index, i in enumerate(node.data.inputs[1:]): - if index != value_index: - edge = chain.edge_to(node.id, i.id) - if edge is not None: - chain.remove_edge(edge) - mutation.signal() - logger.debug( - f"Chain optimization: Removed edge from {node.id} to {i.label}" - ) - - -def optimize(chain: Chain): - max_passes = 10 - for _ in range(max_passes): - mutation = _Mutation() - - __removed_dead_nodes(chain, mutation) - __static_switch_trim(chain, mutation) - - if not mutation.changed: - break diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/custom_types.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/custom_types.py deleted file mode 100644 index 63aed7c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/custom_types.py +++ /dev/null @@ -1,5 +0,0 @@ -from __future__ import annotations - -from typing import Awaitable, Callable, Union - -UpdateProgressFn = Callable[[str, float, Union[float, None]], Awaitable[None]] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/install_server_deps.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/install_server_deps.py deleted file mode 100644 index e33026f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/install_server_deps.py +++ /dev/null @@ -1,96 +0,0 @@ -from __future__ import annotations - -import subprocess -from json import loads as json_parse - -from .store import ( - DependencyInfo, - install_dependencies_sync, - installed_packages, - python_path, -) - -# Get the list of installed packages -# We can't rely on using the package's __version__ attribute because not all packages actually have it -try: - pip_list = subprocess.check_output( - [ - python_path, - "-m", - "pip", - "list", - "--format=json", - "--disable-pip-version-check", - ] - ) - for p in json_parse(pip_list): - installed_packages[p["name"]] = p["version"] -except Exception as e: - print(f"Failed to get installed packages: {e}") - - -deps: list[DependencyInfo] = [ - DependencyInfo( - package_name="sanic", - display_name="Sanic", - version="23.3.0", - from_file="sanic-23.3.0-py3-none-any.whl", - ), - DependencyInfo( - package_name="Sanic-Cors", - version="2.2.0", - from_file="Sanic_Cors-2.2.0-py2.py3-none-any.whl", - ), - # Sanic's downstream deps that are py3-non-any - DependencyInfo( - package_name="aiofiles", - version="23.1.0", - from_file="aiofiles-23.1.0-py3-none-any.whl", - ), - DependencyInfo( - package_name="html5tagger", - version="1.3.0", - from_file="html5tagger-1.3.0-py3-none-any.whl", - ), - DependencyInfo( - package_name="sanic-routing", - version="22.8.0", - from_file="sanic_routing-22.8.0-py3-none-any.whl", - ), - DependencyInfo( - package_name="tracerite", - version="1.1.0", - from_file="tracerite-1.1.0-py3-none-any.whl", - ), - # Sanic's downstream deps that we want to pin anyway - DependencyInfo( - package_name="websockets", - version="11.0.3", - ), - # Other deps necessary for general use - DependencyInfo( - package_name="typing_extensions", - version="4.6.2", - from_file="typing_extensions-4.6.3-py3-none-any.whl", - ), - DependencyInfo( - package_name="pynvml", - version="11.5.0", - from_file="pynvml-11.5.0-py3-none-any.whl", - ), - DependencyInfo( - package_name="chainner-pip", - version="23.2.0", - from_file="chainner_pip-23.2.0-py3-none-any.whl", - ), - DependencyInfo( - package_name="psutil", - version="5.9.5", - ), - DependencyInfo( - package_name="aiohttp", - version="3.9.3", - ), -] - -install_dependencies_sync(deps) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/store.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/store.py deleted file mode 100644 index fd16380..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/store.py +++ /dev/null @@ -1,363 +0,0 @@ -from __future__ import annotations - -import json -import os -import re -import subprocess -import sys -from dataclasses import dataclass -from logging import Logger -from typing import Iterable - -from custom_types import UpdateProgressFn - -python_path = sys.executable -dir_path = os.path.dirname(os.path.realpath(__file__)) - -installed_packages: dict[str, str] = {} - -COLLECTING_REGEX = re.compile(r"Collecting ([a-zA-Z0-9-_]+)") -UNINSTALLING_REGEX = re.compile(r"Uninstalling ([a-zA-Z0-9-_]+)-+") - -DEP_MAX_PROGRESS = 0.8 - -ENV = {**os.environ, "PYTHONIOENCODING": "utf-8"} - - -@dataclass(frozen=True) -class DependencyInfo: - package_name: str - version: str - display_name: str | None = None - from_file: str | None = None - extra_index_url: str | None = None - - -def pin(dependency: DependencyInfo) -> str: - package_name = dependency.package_name - - if dependency.from_file is not None: - whl_file = f"{dir_path}/whls/{package_name}/{dependency.from_file}" - if os.path.isfile(whl_file): - return whl_file - - return f"{package_name}=={dependency.version}" - - -SEMVER_REGEX = re.compile(r"(\d+)(?:\.(\d+)(?:\.(\d+))?)?") - - -def coerce_semver(version: str) -> tuple[int, int, int]: - match = re.search(SEMVER_REGEX, version) - if match: - return ( - int(match.group(1) or 0), - int(match.group(2) or 0), - int(match.group(3) or 0), - ) - return (0, 0, 0) - - -def filter_necessary_to_install(dependencies: Iterable[DependencyInfo]): - """ - Filters out dependencies that are already installed and have the same or higher version. - """ - dependencies_to_install: list[DependencyInfo] = [] - for dependency in dependencies: - version = installed_packages.get(dependency.package_name, None) - if version: - installed_version = coerce_semver(version) - dep_version = coerce_semver(dependency.version) - if installed_version < dep_version: - dependencies_to_install.append(dependency) - elif not version: - dependencies_to_install.append(dependency) - return dependencies_to_install - - -def install_dependencies_sync( - dependencies: list[DependencyInfo], -): - dependencies_to_install = filter_necessary_to_install(dependencies) - if len(dependencies_to_install) == 0: - return 0 - - extra_index_urls = { - dep_info.extra_index_url - for dep_info in dependencies_to_install - if dep_info.extra_index_url - } - - extra_index_args = [] - if len(extra_index_urls) > 0: - extra_index_args.extend(["--extra-index-url", ",".join(extra_index_urls)]) - - exit_code = subprocess.check_call( - [ - python_path, - "-m", - "pip", - "install", - *[pin(dep_info) for dep_info in dependencies_to_install], - "--disable-pip-version-check", - "--no-warn-script-location", - *extra_index_args, - ], - env=ENV, - ) - if exit_code != 0: - raise ValueError("An error occurred while installing dependencies.") - - for dep_info in dependencies_to_install: - installed_packages[dep_info.package_name] = dep_info.version - - return len(dependencies_to_install) - - -async def install_dependencies( - dependencies: list[DependencyInfo], - update_progress_cb: UpdateProgressFn | None = None, - logger: Logger | None = None, -): - # If there's no progress callback, just install the dependencies synchronously - if update_progress_cb is None: - return install_dependencies_sync(dependencies) - - dependencies_to_install = filter_necessary_to_install(dependencies) - if len(dependencies_to_install) == 0: - return 0 - - dependency_name_map = { - dep_info.package_name: dep_info.display_name or dep_info.package_name - for dep_info in dependencies_to_install - } - deps_count = len(dependencies_to_install) - deps_counter = 0 - transitive_deps_counter = 0 - - extra_index_urls = { - dep_info.extra_index_url - for dep_info in dependencies_to_install - if dep_info.extra_index_url - } - - extra_index_args = [] - if len(extra_index_urls) > 0: - extra_index_args.extend(["--extra-index-url", ",".join(extra_index_urls)]) - - def get_progress_amount(): - transitive_progress = 1 - 1 / (2**transitive_deps_counter) - progress = (deps_counter + transitive_progress) / (deps_count + 1) - return min(max(0, progress), 1) * DEP_MAX_PROGRESS - - # Used to increment by a small amount between collect and download - dep_small_incr = (DEP_MAX_PROGRESS / deps_count) / 2 - - process = subprocess.Popen( - [ - python_path, - "-m", - # TODO: Change this back to "pip" once pip updates with my changes - "chainner_pip", - "install", - *[pin(dep_info) for dep_info in dependencies_to_install], - "--disable-chainner_pip-version-check", - "--no-warn-script-location", - "--progress-bar=json", - "--no-cache-dir", - *extra_index_args, - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - encoding="utf-8", - env=ENV, - ) - installing_name = "Unknown" - while True: - nextline = process.stdout.readline() # type: ignore - if process.poll() is not None: - break - line = nextline.strip() - if not line: - continue - - if logger is not None and not line.startswith("Progress:"): - logger.info(line) - - # The Collecting step of pip. It tells us what package is being installed. - if "Collecting" in line: - match = COLLECTING_REGEX.search(line) - if match: - package_name = match.group(1) - installing_name = dependency_name_map.get(package_name, None) - if installing_name is None: - installing_name = package_name - transitive_deps_counter += 1 - else: - deps_counter += 1 - await update_progress_cb( - f"Collecting {installing_name}...", get_progress_amount(), None - ) - # The Downloading step of pip. It tells us what package is currently being downloaded. - # Later, we can use this to get the progress of the download. - # For now, we just tell the user that it's happening. - elif "Downloading" in line: - await update_progress_cb( - f"Downloading {installing_name}...", - get_progress_amount() + dep_small_incr, - None, - ) - # We can parse this line to get the progress of the download, but only in our pip fork for now - elif "Progress:" in line: - json_line = line.replace("Progress:", "").strip() - try: - parsed = json.loads(json_line) - current, total = parsed["current"], parsed["total"] - if total is not None and total > 0: - percent = current / total - await update_progress_cb( - f"Downloading {installing_name}...", - get_progress_amount() + dep_small_incr, - percent, - ) - except Exception as e: - if logger is not None: - logger.error(str(e)) - # pass - # The Installing step of pip. Installs happen for all the collected packages at once. - # We can't get the progress of the installation, so we just tell the user that it's happening. - elif "Installing collected packages" in line: - await update_progress_cb("Installing collected dependencies...", 0.9, None) - - exit_code = process.wait() - if exit_code != 0: - raise ValueError("An error occurred while installing dependencies.") - - await update_progress_cb("Finished installing dependencies...", 1, None) - - for dep_info in dependencies_to_install: - installed_packages[dep_info.package_name] = dep_info.version - - return len(dependencies_to_install) - - -def uninstall_dependencies_sync( - dependencies: list[DependencyInfo], -): - if len(dependencies) == 0: - return - - exit_code = subprocess.check_call( - [ - python_path, - "-m", - "pip", - "uninstall", - *[d.package_name for d in dependencies], - "-y", - ], - env=ENV, - ) - if exit_code != 0: - raise ValueError("An error occurred while uninstalling dependencies.") - - for dep_info in dependencies: - installed_packages[dep_info.package_name] = dep_info.version - - -async def uninstall_dependencies( - dependencies: list[DependencyInfo], - update_progress_cb: UpdateProgressFn | None = None, - logger: Logger | None = None, -): - # If there's no progress callback, just uninstall the dependencies synchronously - if update_progress_cb is None: - return uninstall_dependencies_sync(dependencies) - - if len(dependencies) == 0: - return - - dependency_name_map = { - dep_info.package_name: dep_info.display_name or dep_info.package_name - for dep_info in dependencies - } - deps_count = len(dependencies) - deps_counter = 0 - transitive_deps_counter = 0 - - def get_progress_amount(): - transitive_progress = 1 - 1 / (2**transitive_deps_counter) - progress = (deps_counter + transitive_progress) / (deps_count + 1) - return min(max(0, progress), 1) - - # Used to increment by a small amount between collect and download - dep_small_incr = (1 / deps_count) / 2 - - process = subprocess.Popen( - [ - python_path, - "-m", - # TODO: Change this back to "pip" once pip updates with my changes - "chainner_pip", - "uninstall", - *[d.package_name for d in dependencies], - "-y", - ], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - encoding="utf-8", - env=ENV, - ) - uninstalling_name = "Unknown" - while True: - nextline = process.stdout.readline() # type: ignore - if process.poll() is not None: - break - line = nextline.strip() - if not line: - continue - - if logger is not None and not line.startswith("Progress:"): - logger.info(line) - - # The Uninstalling step of pip. It tells us what package is being UNinstalled. - if "Uninstalling" in line: - match = UNINSTALLING_REGEX.search(line) - if match: - package_name = match.group(1) - uninstalling_name = dependency_name_map.get(package_name, None) - if uninstalling_name is None: - uninstalling_name = package_name - transitive_deps_counter += 1 - else: - deps_counter += 1 - await update_progress_cb( - f"Uninstalling {uninstalling_name}...", get_progress_amount(), None - ) - # The Downloading step of pip. It tells us what package is currently being downloaded. - # Later, we can use this to get the progress of the download. - # For now, we just tell the user that it's happening. - elif "Successfully uninstalled" in line: - await update_progress_cb( - f"Uninstalled {uninstalling_name}.", - get_progress_amount() + dep_small_incr, - None, - ) - - exit_code = process.wait() - if exit_code != 0: - raise ValueError("An error occurred while installing dependencies.") - - await update_progress_cb("Finished installing dependencies...", 1, None) - - for dep_info in dependencies: - del installed_packages[dep_info.package_name] - - -__all__ = [ - "DependencyInfo", - "python_path", - "install_dependencies", - "install_dependencies_sync", - "installed_packages", -] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/README.md b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/README.md deleted file mode 100644 index 1b8cc97..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Bundled Wheels - -This is where we can store wheel files that are to be bundled with chaiNNer, in order to avoid needing to download them. - -## Requirements - -Bundled wheels must be - -1. Reasonably small (a few MB max) -2. py3-none-any (compatible with any python version and device) -3. License compatible (allows bundling) - -## Goals - -- Speed up initial start time by downloading the minimal number of wheel files from the internet -- Not increase chaiNNer's bundle size too much - -## Structure - -The `whls` folder shall contain individual folders, named according to the package name we use to install via pip. Inside the folder must be the .whl file as well as the project's license. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/Sanic-Cors/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/Sanic-Cors/LICENSE deleted file mode 100644 index 35740e3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/Sanic-Cors/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-present Sanic Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/Sanic-Cors/Sanic_Cors-2.2.0-py2.py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/Sanic-Cors/Sanic_Cors-2.2.0-py2.py3-none-any.whl deleted file mode 100644 index c983eca..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/Sanic-Cors/Sanic_Cors-2.2.0-py2.py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/aiofiles/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/aiofiles/LICENSE deleted file mode 100644 index e06d208..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/aiofiles/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/aiofiles/aiofiles-23.1.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/aiofiles/aiofiles-23.1.0-py3-none-any.whl deleted file mode 100644 index 6d0bc93..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/aiofiles/aiofiles-23.1.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/chainner-pip/chainner_pip-23.2.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/chainner-pip/chainner_pip-23.2.0-py3-none-any.whl deleted file mode 100644 index 6c20181..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/chainner-pip/chainner_pip-23.2.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/html5tagger/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/html5tagger/LICENSE deleted file mode 100644 index b3dbff0..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/html5tagger/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or -distribute this software, either in source code form or as a compiled -binary, for any purpose, commercial or non-commercial, and by any -means. - -In jurisdictions that recognize copyright laws, the author or authors -of this software dedicate any and all copyright interest in the -software to the public domain. We make this dedication for the benefit -of the public at large and to the detriment of our heirs and -successors. We intend this dedication to be an overt act of -relinquishment in perpetuity of all present and future rights to this -software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/html5tagger/html5tagger-1.3.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/html5tagger/html5tagger-1.3.0-py3-none-any.whl deleted file mode 100644 index b2c182d..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/html5tagger/html5tagger-1.3.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/pynvml/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/pynvml/LICENSE deleted file mode 100644 index cd2e942..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/pynvml/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011-2021, NVIDIA Corporation. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of staged-recipes nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/pynvml/pynvml-11.5.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/pynvml/pynvml-11.5.0-py3-none-any.whl deleted file mode 100644 index 0752b8f..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/pynvml/pynvml-11.5.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic-routing/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic-routing/LICENSE deleted file mode 100644 index 35740e3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic-routing/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-present Sanic Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic-routing/sanic_routing-22.8.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic-routing/sanic_routing-22.8.0-py3-none-any.whl deleted file mode 100644 index b708e23..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic-routing/sanic_routing-22.8.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic/LICENSE deleted file mode 100644 index 35740e3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-present Sanic Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic/sanic-23.3.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic/sanic-23.3.0-py3-none-any.whl deleted file mode 100644 index fd6fac1..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/sanic/sanic-23.3.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/tracerite/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/tracerite/LICENSE deleted file mode 100644 index 35740e3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/tracerite/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2016-present Sanic Community - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/tracerite/tracerite-1.1.0-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/tracerite/tracerite-1.1.0-py3-none-any.whl deleted file mode 100644 index 4a74422..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/tracerite/tracerite-1.1.0-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/typing_extensions/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/typing_extensions/LICENSE deleted file mode 100644 index f26bcf4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/typing_extensions/LICENSE +++ /dev/null @@ -1,279 +0,0 @@ -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations, which became -Zope Corporation. In 2001, the Python Software Foundation (PSF, see -https://www.python.org/psf/) was formed, a non-profit organization -created specifically to own Python-related Intellectual Property. -Zope Corporation was a sponsoring member of the PSF. - -All Python releases are Open Source (see https://opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2 and above 2.1.1 2001-now PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -Python software and documentation are licensed under the -Python Software Foundation License Version 2. - -Starting with Python 3.8.6, examples, recipes, and other code in -the documentation are dual licensed under the PSF License Version 2 -and the Zero-Clause BSD license. - -Some software incorporated into Python is under different licenses. -The licenses are listed with code falling under that license. - - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; -All Rights Reserved" are retained in Python alone or in any derivative version -prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION ----------------------------------------------------------------------- - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/typing_extensions/typing_extensions-4.6.3-py3-none-any.whl b/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/typing_extensions/typing_extensions-4.6.3-py3-none-any.whl deleted file mode 100644 index bce86d2..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/dependencies/whls/typing_extensions/typing_extensions-4.6.3-py3-none-any.whl and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/events.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/events.py deleted file mode 100644 index b7d1d74..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/events.py +++ /dev/null @@ -1,160 +0,0 @@ -from __future__ import annotations - -import asyncio -from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import Dict, Literal, TypedDict, Union - -from api import ErrorValue, InputId, NodeId, OutputId - -# General events - - -class BackendStatusData(TypedDict): - message: str - progress: float - statusProgress: float | None - - -class BackendStatusEvent(TypedDict): - event: Literal["backend-status", "package-install-status"] - data: BackendStatusData - - -class BackendStateEvent(TypedDict): - event: Literal["backend-started"] - data: None - - -BackendEvent = Union[BackendStatusEvent, BackendStateEvent] - - -# Execution events - - -InputsDict = Dict[InputId, ErrorValue] - - -class ExecutionErrorSource(TypedDict): - nodeId: NodeId - schemaId: str - inputs: InputsDict - - -class ExecutionErrorData(TypedDict): - message: str - exception: str - exceptionTrace: str - source: ExecutionErrorSource | None - - -class ExecutionErrorEvent(TypedDict): - event: Literal["execution-error"] - data: ExecutionErrorData - - -class ChainStartData(TypedDict): - nodes: list[str] - - -class ChainStartEvent(TypedDict): - event: Literal["chain-start"] - data: ChainStartData - - -class NodeStartData(TypedDict): - nodeId: NodeId - - -class NodeStartEvent(TypedDict): - event: Literal["node-start"] - data: NodeStartData - - -class NodeProgressData(TypedDict): - nodeId: NodeId - progress: float - """A number between 0 and 1""" - index: int - total: int - eta: float - - -class NodeProgressUpdateEvent(TypedDict): - event: Literal["node-progress"] - data: NodeProgressData - - -class NodeBroadcastData(TypedDict): - nodeId: NodeId - data: dict[OutputId, object] - types: dict[OutputId, object] - - -class NodeBroadcastEvent(TypedDict): - event: Literal["node-broadcast"] - data: NodeBroadcastData - - -class NodeFinishData(TypedDict): - nodeId: NodeId - executionTime: float - - -class NodeFinishEvent(TypedDict): - event: Literal["node-finish"] - data: NodeFinishData - - -ExecutionEvent = Union[ - ExecutionErrorEvent, - ChainStartEvent, - NodeStartEvent, - NodeProgressUpdateEvent, - NodeBroadcastEvent, - NodeFinishEvent, -] - - -Event = Union[ExecutionEvent, BackendEvent] - - -class EventConsumer(ABC): - @abstractmethod - async def put(self, event: Event) -> None: ... - - @staticmethod - def filter(queue: EventConsumer, allowed: set[str]) -> EventConsumer: - return _FilteredEventConsumer(queue, allowed) - - -@dataclass -class _FilteredEventConsumer(EventConsumer): - queue: EventConsumer - allowed: set[str] - - async def put(self, event: Event) -> None: - if event["event"] in self.allowed: - await self.queue.put(event) - - -class EventQueue(EventConsumer): - def __init__(self): - self.queue = asyncio.Queue() - - async def get(self) -> Event: - return await self.queue.get() - - async def put(self, event: Event) -> None: - await self.queue.put(event) - - async def wait_until_empty(self, timeout: float) -> None: - while timeout > 0: - if self.queue.empty(): - return - await asyncio.sleep(0.01) - timeout -= 0.01 - - async def put_and_wait(self, event: Event, timeout: float = float("inf")) -> None: - await self.queue.put(event) - await self.wait_until_empty(timeout) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Apache License.txt b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Apache License.txt deleted file mode 100644 index 989e2c5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Apache License.txt +++ /dev/null @@ -1,201 +0,0 @@ -Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto-Light.ttf b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto-Light.ttf deleted file mode 100644 index 94c6bcc..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto-Light.ttf and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/LICENSE.txt b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/LICENSE.txt deleted file mode 100644 index d645695..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Bold.ttf b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Bold.ttf deleted file mode 100644 index 43da14d..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Bold.ttf and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-BoldItalic.ttf b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-BoldItalic.ttf deleted file mode 100644 index bcfdab4..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-BoldItalic.ttf and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Italic.ttf b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Italic.ttf deleted file mode 100644 index 1b5eaa3..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Italic.ttf and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Regular.ttf b/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Regular.ttf deleted file mode 100644 index ddf4bfa..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/fonts/Roboto/Roboto-Regular.ttf and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/linux-cli-requirements.txt b/MangaJaNaiConverterGui/chaiNNer/backend/src/linux-cli-requirements.txt deleted file mode 100644 index 672cc5f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/linux-cli-requirements.txt +++ /dev/null @@ -1,47 +0,0 @@ -aiofiles==23.2.1 -chainner_ext==0.3.10 -dill==0.3.8 -einops==0.8.0 -filelock==3.14.0 -fsspec==2024.5.0 -html5tagger==1.3.0 -httptools==0.6.1 -Jinja2==3.1.4 -MarkupSafe==2.1.5 -mpmath==1.3.0 -multidict==6.0.5 -multiprocess==0.70.16 -networkx==3.3 -numpy==1.26.4 -nvidia-cublas-cu12==12.1.3.1 -nvidia-cuda-cupti-cu12==12.1.105 -nvidia-cuda-nvrtc-cu12==12.1.105 -nvidia-cuda-runtime-cu12==12.1.105 -nvidia-cudnn-cu12==8.9.2.26 -nvidia-cufft-cu12==11.0.2.54 -nvidia-curand-cu12==10.3.2.106 -nvidia-cusolver-cu12==11.4.5.107 -nvidia-cusparse-cu12==12.1.0.106 -nvidia-nccl-cu12==2.20.5 -nvidia-nvjitlink-cu12==12.5.40 -nvidia-nvtx-cu12==12.1.105 -opencv-python==4.9.0.80 -pillow==10.3.0 -psutil==5.9.8 -pynvml==11.5.0 -rarfile==4.2 -safetensors==0.4.3 -sanic==23.12.1 -sanic-routing==23.12.0 -spandrel==0.3.4 -spandrel_extra_arches==0.1.1 -sympy==1.12 -torch==2.3.0 -torchaudio==2.3.0 -torchvision==0.18.0 -tracerite==1.1.1 -triton==2.3.0 -typing_extensions==4.12.0 -ujson==5.10.0 -uvloop==0.19.0 -websockets==12.0 \ No newline at end of file diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/node_check.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/node_check.py deleted file mode 100644 index 83a5d45..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/node_check.py +++ /dev/null @@ -1,298 +0,0 @@ -from __future__ import annotations - -import ast -import inspect -import os -import pathlib -from enum import Enum -from typing import Any, Callable, Dict, List, NewType, Set, Union, cast, get_args - -from custom_types import NodeType -from nodes.base_input import BaseInput -from nodes.base_output import BaseOutput - -_Ty = NewType("_Ty", object) - - -class CheckFailedError(Exception): - pass - - -class CheckLevel(Enum): - NONE = "none" - WARN = "warn" - FIX = "fix" - ERROR = "error" - - @staticmethod - def parse(s: str) -> CheckLevel: - s = s.strip().lower() - if s == CheckLevel.NONE.value: - return CheckLevel.NONE - elif s == CheckLevel.WARN.value: - return CheckLevel.WARN - elif s == CheckLevel.FIX.value: - return CheckLevel.FIX - elif s == CheckLevel.ERROR.value: - return CheckLevel.ERROR - else: - raise ValueError(f"Invalid check level: {s}") - - -def _get_check_level(name: str, default: CheckLevel) -> CheckLevel: - try: - s = os.environ.get(name, default.value) - return CheckLevel.parse(s) - except: - return default - - -CHECK_LEVEL = _get_check_level("CHECK_LEVEL", CheckLevel.NONE) -NAME_CHECK_LEVEL = _get_check_level("NAME_CHECK_LEVEL", CHECK_LEVEL) -TYPE_CHECK_LEVEL = _get_check_level("TYPE_CHECK_LEVEL", CHECK_LEVEL) - - -class TypeTransformer(ast.NodeTransformer): - def visit_BinOp(self, node: ast.BinOp): - if isinstance(node.op, ast.BitOr): - return ast.Subscript( - value=ast.Name(id="Union", ctx=ast.Load()), - slice=ast.Index( - value=ast.Tuple( - elts=[ - self.visit(node.left), - self.visit(node.right), - ], - ctx=ast.Load(), - ), - ctx=ast.Load(), - ), - ctx=ast.Load(), - ) - return super().visit_BinOp(node) - - -def compile_type_string(s: str, filename: str = ""): - tree = ast.parse(s, filename, "eval") - new_tree = ast.fix_missing_locations(TypeTransformer().visit(tree)) - return compile(new_tree, filename, "eval") - - -def eval_type(t: str | _Ty, __globals: dict[str, Any]): - if not isinstance(t, str): - return t - - # `compile_type_string` adds `Union`, so we need it in scope - local_scope = { - "Union": Union, - } - - try: - # pylint: disable=eval-used - return _Ty(eval(compile_type_string(t), __globals, local_scope)) - except Exception as e: - raise ValueError(f"Unable to evaluate type '{t}': {e}") from e - - -def union_types(types: List[_Ty]) -> _Ty: - assert len(types) > 0 - t: Any = types[0] - for t2 in types[1:]: - t = Union[t, cast(Any, t2)] - return t - - -def union_to_set(t: _Ty) -> Set[_Ty]: - s = str(t) - if s.startswith("typing.Union["): - return set(get_args(t)) - elif s.startswith("typing.Optional["): - return {*union_to_set(get_args(t)[0]), _Ty(type(None))} - else: - return {t} - - -def is_subset_of(a: _Ty, b: _Ty) -> bool: - if a == b: - return True - - return union_to_set(a).issubset(union_to_set(b)) - - -def get_type_annotations(fn: Callable) -> Dict[str, _Ty]: - """Get the annotations for a function, with support for Python 3.8+""" - ann = getattr(fn, "__annotations__", None) - - if ann is None: - return {} - - type_annotations: Dict[str, _Ty] = {} - for k, v in ann.items(): - type_annotations[k] = eval_type(v, fn.__globals__) - return type_annotations - - -def validate_return_type(return_type: _Ty, outputs: list[BaseOutput]): - if str(return_type).startswith("api.Iterator["): - return_type = get_args(return_type)[0] - elif str(return_type).startswith("api.Collector["): - return_type = get_args(return_type)[1] - - if len(outputs) == 0: - if return_type is not None and return_type is not type(None): # type: ignore - raise CheckFailedError( - f"Return type should be 'None' because there are no outputs" - ) - elif len(outputs) == 1: - o = outputs[0] - if o.associated_type is not None and not is_subset_of( - return_type, o.associated_type - ): - raise CheckFailedError( - f"Return type '{return_type}' must be a subset of '{o.associated_type}'" - ) - else: - if not str(return_type).startswith("typing.Tuple["): - raise CheckFailedError( - f"Return type '{return_type}' must be a tuple because there are multiple outputs" - ) - - return_args = get_args(return_type) - if len(return_args) != len(outputs): - raise CheckFailedError( - f"Return type '{return_type}' must have the same number of arguments as there are outputs" - ) - - for o, return_arg in zip(outputs, return_args): - if o.associated_type is not None and not is_subset_of( - return_arg, o.associated_type - ): - raise CheckFailedError( - f"Return type of {o.label} '{return_arg}' must be a subset of '{o.associated_type}'" - ) - - -def check_schema_types( - wrapped_func: Callable, - node_type: NodeType, - inputs: list[BaseInput], - outputs: list[BaseOutput], -): - """ - Runtime validation for the number of inputs/outputs compared to the type args - """ - - ann = get_type_annotations(wrapped_func) - - # check return type - if "return" in ann: - validate_return_type(ann.pop("return"), outputs) - - # check inputs - - arg_spec = inspect.getfullargspec(wrapped_func) - for arg in arg_spec.args: - if not arg in ann: - raise CheckFailedError(f"Missing type annotation for '{arg}'") - - if node_type == "iteratorHelper": - # iterator helpers have inputs that do not describe the arguments of the function, so we can't check them - return - - if node_type == "iterator": - # the last argument of an iterator is the iterator context, so we have to account for that - context = [*ann.keys()][-1] - context_type = ann.pop(context) - if str(context_type) != "": - raise CheckFailedError( - f"Last argument of an iterator must be an IteratorContext, not '{context_type}'" - ) - - if arg_spec.varargs is not None: - if not arg_spec.varargs in ann: - raise CheckFailedError(f"Missing type annotation for '{arg_spec.varargs}'") - va_type = ann.pop(arg_spec.varargs) - - # split inputs by varargs and non-varargs - varargs_inputs = inputs[len(ann) :] - inputs = inputs[: len(ann)] - - total: list[_Ty] | None = [] - for i in varargs_inputs: - associated_type = i.associated_type - - if associated_type is not None: - if not is_subset_of(associated_type, va_type): - raise CheckFailedError( - f"Input type of {i.label} '{associated_type}' is not assignable to varargs type '{va_type}'" - ) - - # append to total - if associated_type is not None: - if total is not None: - total.append(associated_type) - else: - total = None - - if total is not None: - total_type = union_types(total) - if total_type != va_type: - raise CheckFailedError( - f"Varargs type '{va_type}' should be equal to the union of all arguments '{total_type}'" - ) - - if len(ann) != len(inputs): - raise CheckFailedError( - f"Number of inputs and arguments don't match: {len(ann)=} != {len(inputs)=}" - ) - for (a_name, a_type), i in zip(ann.items(), inputs): - associated_type = i.associated_type - if associated_type is not None and a_type != associated_type: - raise CheckFailedError( - f"Expected type of {i.label} ({a_name}) to be '{associated_type}' but found '{a_type}'" - ) - - -def check_naming_conventions( - wrapped_func: Callable, - node_type: NodeType, - name: str, - fix: bool, -): - expected_name = ( - name.lower() - .replace(" (iterator)", "") - .replace(" ", "_") - .replace("-", "_") - .replace("(", "") - .replace(")", "") - .replace("&", "and") - ) - - if node_type == "iteratorHelper": - expected_name = "iterator_helper_" + expected_name - - func_name = wrapped_func.__name__ - file_path = pathlib.Path(inspect.getfile(wrapped_func)) - file_name = file_path.stem - - # check function name - if func_name != expected_name + "_node": - if not fix: - raise CheckFailedError( - f"Function name is '{func_name}', but it should be '{expected_name}_node'" - ) - - fixed_code = file_path.read_text(encoding="utf-8").replace( - f"def {func_name}(", f"def {expected_name}_node(" - ) - file_path.write_text(fixed_code, encoding="utf-8") - - # check file name - if node_type != "iteratorHelper" and file_name != expected_name: - if not fix: - raise CheckFailedError( - f"File name is '{file_name}.py', but it should be '{expected_name}.py'" - ) - - os.rename(file_path, file_path.with_name(expected_name + ".py")) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/caption.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/caption.py deleted file mode 100644 index 2d8546d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/caption.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -import os -import sys -from enum import Enum - -import numpy as np -from PIL import Image, ImageDraw, ImageFont - -from ..utils.utils import get_h_w_c -from .image_utils import as_target_channels, normalize - - -class CaptionPosition(Enum): - BOTTOM = "bottom" - TOP = "top" - - -def get_font_size(font: ImageFont.FreeTypeFont, text: str) -> tuple[int, int]: - """Get font [width, height] of the given text""" - # (left, top, right, bottom) - caption_bb = font.getbbox(text) - font_width = caption_bb[2] - caption_bb[0] - font_height = caption_bb[3] - caption_bb[1] - return font_width, font_height - - -def get_font(font_size: int): - font_path = os.path.join( - os.path.dirname(sys.modules["__main__"].__file__), # type: ignore - "fonts/Roboto-Light.ttf", # type: ignore - ) - return ImageFont.truetype(font_path, font_size) - - -def add_caption( - img: np.ndarray, caption: str, size: int, position: CaptionPosition -) -> np.ndarray: - """Add caption with PIL""" - _, w, c = get_h_w_c(img) - - cap_img = Image.fromarray(np.zeros((size, w), dtype=np.uint8)) - - font_size = round(size * 0.8) - font = get_font(font_size) - - fw, _ = get_font_size(font, caption) - # scale font size to fit image - if fw > w: - font = get_font(round(font_size * w / fw)) - - d = ImageDraw.Draw(cap_img) - d.text( - (w // 2, size // 2), - caption, - font=font, - anchor="mm", - align="center", - fill=255, - ) - - cap_img = normalize(np.array(cap_img)) - cap_img = as_target_channels(cap_img, c) - - if position == CaptionPosition.BOTTOM: - return np.vstack((img, cap_img)) - elif position == CaptionPosition.TOP: - return np.vstack((cap_img, img)) - else: - raise ValueError(f"Unknown position {position}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/cas.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/cas.py deleted file mode 100644 index d32be8f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/cas.py +++ /dev/null @@ -1,58 +0,0 @@ -import cv2 -import numpy as np - -from ..utils.utils import get_h_w_c -from .image_utils import as_2d_grayscale - - -def _luminance(img: np.ndarray) -> np.ndarray: - """Returns the luminance of an image.""" - _, _, c = get_h_w_c(img) - if c == 1: - return as_2d_grayscale(img) - if c == 2: - return img[..., 0] - return np.dot(img[..., :3], [0.2126, 0.7152, 0.0722]) - - -def create_cas_mask(img: np.ndarray, kernel: np.ndarray, bias: float = 2) -> np.ndarray: - """ - Uses contrast adaptive sharpening's method to create a mask to interpolate between the original - and sharpened image. - - `kernel` is an element create by `cv2.getStructuringElement`. It determines the shape and size - of each pixel's neighborhood. - - `bias` is used to bias the mask towards the more sharpening. A value of 1 means no bias, a - value greater than 1 biases the mask towards the sharpened image, a value less than 1 biases - the mask towards the original image. - - Reference: - Lou Kramer, FidelityFX CAS, AMD Developer Day 2019, https://gpuopen.com/wp-content/uploads/2019/07/FidelityFX-CAS.pptx - https://www.shadertoy.com/view/wtlSWB# - """ - assert bias > 0, "Bias must be greater than or equal to 0." - - l = _luminance(img) - min_l = cv2.erode(l, kernel) - max_l = cv2.dilate(l, kernel) - min_d = np.minimum(1.0 - max_l, min_l, out=min_l) # type: ignore - max_l += 1e-8 # type: ignore - min_d /= max_l - mask = min_d - if bias != 1: - mask = np.power(mask, 1 / bias, out=mask) - return mask - - -def cas_mix( - img: np.ndarray, - sharpened: np.ndarray, - kernel: np.ndarray, - bias: float = 2, -) -> np.ndarray: - mask = create_cas_mask(img, kernel, bias) - _, _, c = get_h_w_c(sharpened) - if c > 1: - mask = np.dstack((mask,) * c) - return img * (1 - mask) + sharpened * mask # type: ignore diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/linear_histogram.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/linear_histogram.py deleted file mode 100644 index 9392e67..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/linear_histogram.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -import numpy as np - -__author__ = "Daniel Steinberg" -__copyright__ = "Copyright 2022, Daniel Steinberg" -__credits__ = ["Daniel Steinberg"] -__license__ = "MIT" -__version__ = "1.0.0" -__maintainer__ = "Daniel Steinberg" -__link__ = "https://github.com/dstein64/colortrans" - - -def matrix_sqrt(x: np.ndarray): - eig_val, eig_vec = np.linalg.eig(x) - return eig_vec.dot(np.diag(np.sqrt(eig_val.clip(min=0)))).dot(eig_vec.T) - - -def linear_histogram_transfer( - img: np.ndarray, - ref_img: np.ndarray, - valid_indices: np.ndarray, - ref_valid_indices: np.ndarray, -) -> np.ndarray: - """ - Transfers the color distribution from the source to the target image - using the Linear Histogram Matching.. - - This implementation is based on to the Hertzmann, Aaron. "Algorithms - for Rendering in Artistic Styles." Ph.D., New York University, 2001. - """ - - shape = img.shape - - # Convert HxWxC image to a (H*W)xC matrix. - content = img.reshape(-1, shape[-1]) - - valid_content = img[valid_indices] - valid_reference = ref_img[ref_valid_indices] - - mu_content = np.mean(valid_content, axis=0) - mu_reference = np.mean(valid_reference, axis=0) - - cov_content = np.cov(valid_content, rowvar=False) - cov_reference = np.cov(valid_reference, rowvar=False) - - transfer = matrix_sqrt(cov_reference) - sqrt_cov_content = matrix_sqrt(cov_content) - - if np.linalg.det(sqrt_cov_content) == 0: - # Singular matrix: modify it by an arbitrary value before calculating the inverse matrix - sqrt_cov_content += ( - np.identity(sqrt_cov_content.shape[-1], sqrt_cov_content.dtype) / 255.0 - ) - sqrt_cov_content_inv = np.linalg.inv(sqrt_cov_content) - - transfer = transfer.dot(sqrt_cov_content_inv) - transfer = transfer.dot((content - mu_content).T).T - transfer = transfer + mu_reference - - # Restore image dimensions. - transfer = transfer.reshape(img.shape).clip(0, 1) - - return transfer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/mean_std.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/mean_std.py deleted file mode 100644 index 89612dc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/mean_std.py +++ /dev/null @@ -1,189 +0,0 @@ -"""Fast color transfer method using mean and std""" - -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -__author__ = "Adrian Rosebrock" -__copyright__ = "Copyright 2014, Adrian Rosebrock" -__credits__ = ["Adrian Rosebrock", "theflyingzamboni"] -__license__ = "MIT" -__version__ = "1.0.0" -__maintainer__ = "Adrian Rosebrock" -__link__ = "https://github.com/jrosebr1/color_transfer" - - -class TransferColorSpace(Enum): - LAB = "L*a*b*" - RGB = "RGB" - - -class OverflowMethod(Enum): - CLIP = 1 - SCALE = 0 - - -def image_stats(img: np.ndarray): - """Get means and standard deviations of channels""" - - # Compute the mean and standard deviation of each channel - channel_a, channel_b, channel_c = np.split(img, 3, 1) - a_mean, a_std = (channel_a.mean(), channel_a.std()) - b_mean, b_std = (channel_b.mean(), channel_b.std()) - c_mean, c_std = (channel_c.mean(), channel_c.std()) - - # Return the color statistics - return a_mean, a_std, b_mean, b_std, c_mean, c_std - - -def min_max_scale( - img: np.ndarray, - valid_indices: np.ndarray, - new_range: tuple[float, float] = (0, 255), -): - """Perform min-max scaling to a NumPy array""" - - # Get arrays current min and max - mn = img[valid_indices].min() - mx = img[valid_indices].max() - - # Check if scaling needs to be done to be in new_range - if mn < new_range[0] or mx > new_range[1]: - # Perform min-max scaling - range_diff = new_range[1] - new_range[0] - scaled = range_diff * (img - mn) / (mx - mn) + new_range[0] - else: - # Return array if already in range - scaled = img - - return scaled - - -def scale_array( - arr: np.ndarray, - overflow_method: OverflowMethod, - valid_indices: np.ndarray, - clip_min: int = 0, - clip_max: int = 255, -) -> np.ndarray: - """ - Trim NumPy array values to be in [0, 255] range with option of - clipping or scaling. - """ - - if overflow_method == OverflowMethod.CLIP: - scaled = np.clip(arr, clip_min, clip_max) - else: - scale_range = ( - max([arr[valid_indices].min(), clip_min]), - min([arr[valid_indices].max(), clip_max]), - ) - scaled = min_max_scale(arr, new_range=scale_range, valid_indices=valid_indices) - - return scaled - - -def mean_std_transfer( - img: np.ndarray, - ref_img: np.ndarray, - colorspace: TransferColorSpace, - overflow_method: OverflowMethod, - valid_indices: np.ndarray, - ref_valid_indices: np.ndarray, - reciprocal_scale: bool = True, -) -> np.ndarray: - """ - Transfers the color distribution from the source to the target image. - Uses the mean and standard deviations of the specified - colorspace. This implementation is (loosely) based on to the - "Color Transfer between Images" paper by Reinhard et al., 2001. - """ - - a_clip_min, a_clip_max, b_clip_min, b_clip_max, c_clip_min, c_clip_max = ( - 0, - 0, - 0, - 0, - 0, - 0, - ) - - # Convert the images from the RGB to L*a*b* color space, if necessary - if colorspace == TransferColorSpace.LAB: - a_clip_min, a_clip_max = (0, 100) - b_clip_min, b_clip_max = (-127, 127) - c_clip_min, c_clip_max = (-127, 127) - img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) - ref_img = cv2.cvtColor(ref_img, cv2.COLOR_BGR2LAB) - elif colorspace == TransferColorSpace.RGB: - a_clip_min, a_clip_max = (0, 1) - b_clip_min, b_clip_max = (0, 1) - c_clip_min, c_clip_max = (0, 1) - img = img[:, :, :3] - ref_img = ref_img[:, :, :3] - else: - raise ValueError(f"Invalid color space {colorspace}") - - # Compute color statistics for the source and target images - ( - a_mean_tar, - a_std_tar, - b_mean_tar, - b_std_tar, - c_mean_tar, - c_std_tar, - ) = image_stats(img[valid_indices]) - ( - a_mean_src, - a_std_src, - b_mean_src, - b_std_src, - c_mean_src, - c_std_src, - ) = image_stats(ref_img[ref_valid_indices]) - - # Subtract the means from the target image - channel_a, channel_b, channel_c = cv2.split(img) - channel_a -= a_mean_tar - channel_b -= b_mean_tar - channel_c -= c_mean_tar - - if reciprocal_scale: - # Scale by the standard deviations using reciprocal of paper proposed factor - channel_a = (a_std_src / a_std_tar) * channel_a - channel_b = (b_std_src / b_std_tar) * channel_b - channel_c = (c_std_src / c_std_tar) * channel_c - else: - # Scale by the standard deviations using paper proposed factor - channel_a = (a_std_tar / a_std_src) * channel_a - channel_b = (b_std_tar / b_std_src) * channel_b - channel_c = (c_std_tar / c_std_src) * channel_c - - # Add in the source mean - channel_a += a_mean_src - channel_b += b_mean_src - channel_c += c_mean_src - - # Clip/scale the pixel intensities to [clip_min, clip_max] if they fall - # outside this range - channel_a = scale_array( - channel_a, overflow_method, valid_indices, a_clip_min, a_clip_max - ) - channel_b = scale_array( - channel_b, overflow_method, valid_indices, b_clip_min, b_clip_max - ) - channel_c = scale_array( - channel_c, overflow_method, valid_indices, c_clip_min, c_clip_max - ) - - # Merge the channels together, then convert back to the RGB color - # space if necessary - transfer = cv2.merge([channel_a, channel_b, channel_c]) - if colorspace == TransferColorSpace.LAB: - transfer = cv2.cvtColor(transfer, cv2.COLOR_LAB2BGR) - - # Return the color transferred image - return transfer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/principal_color.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/principal_color.py deleted file mode 100644 index 98aa3cf..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/color_transfer/principal_color.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import annotations - -import numpy as np - -__author__ = "Daniel Steinberg" -__copyright__ = "Copyright 2022, Daniel Steinberg" -__credits__ = ["Daniel Steinberg"] -__license__ = "MIT" -__version__ = "1.0.0" -__maintainer__ = "Daniel Steinberg" -__link__ = "https://github.com/dstein64/colortrans" - - -def principal_color_transfer( - img: np.ndarray, - ref_img: np.ndarray, - valid_indices: np.ndarray, - ref_valid_indices: np.ndarray, -) -> np.ndarray: - """ - Transfers the color distribution from the source to the target image using - Principal Component Color Matching. - - This implementation is based on: - - Kotera, Hiroaki, Hung-Shing Chen, and Tetsuro Morimoto. - "Object-to-Object Color Mapping by Image Segmentation." In Color Imaging: - Device-Independent Color, Color Hardcopy, and Graphic Arts IV, 3648:148-57. - SPIE, 1998. - - Kotera, Hiroaki. "A Scene-Referred Color Transfer for Pleasant Imaging - on Display." In IEEE International Conference on Image Processing 2005, - 2:II-5, 2005. - """ - - shape = img.shape - - # Convert HxWxC image to a (H*W)xC matrix. - content = img.reshape(-1, shape[-1]) - ref_img.reshape(-1, shape[-1]) - - valid_content = img[valid_indices] - valid_reference = ref_img[ref_valid_indices] - - mu_content = np.mean(valid_content, axis=0) - mu_reference = np.mean(valid_reference, axis=0) - - cov_content = np.cov(valid_content, rowvar=False) - cov_reference = np.cov(valid_reference, rowvar=False) - - eigval_content, eigvec_content = np.linalg.eig(cov_content) - eigval_reference, eigvec_reference = np.linalg.eig(cov_reference) - - # Division by 0 is forbidden: change null values to arbitrary values - eigval_content = np.where(eigval_content == 0, 1e-42, eigval_content) - eigval_factor = eigval_reference / eigval_content - - scaling = np.diag(np.sqrt(eigval_factor.clip(min=0))) - transform = eigvec_reference.dot(scaling).dot(eigvec_content.T) - transfer = (content - mu_content).dot(transform.T) + mu_reference - - # Restore image dimensions. - transfer = transfer.reshape(shape).clip(0, 1) - - return transfer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/format.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/format.py deleted file mode 100644 index cfb4fd4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/format.py +++ /dev/null @@ -1,243 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Union, cast - -DxgiFormat = Literal[ - "UNKNOWN", - "R32G32B32A32_TYPELESS", - "R32G32B32A32_FLOAT", - "R32G32B32A32_UINT", - "R32G32B32A32_SINT", - "R32G32B32_TYPELESS", - "R32G32B32_FLOAT", - "R32G32B32_UINT", - "R32G32B32_SINT", - "R16G16B16A16_TYPELESS", - "R16G16B16A16_FLOAT", - "R16G16B16A16_UNORM", - "R16G16B16A16_UINT", - "R16G16B16A16_SNORM", - "R16G16B16A16_SINT", - "R32G32_TYPELESS", - "R32G32_FLOAT", - "R32G32_UINT", - "R32G32_SINT", - "R32G8X24_TYPELESS", - "D32_FLOAT_S8X24_UINT", - "R32_FLOAT_X8X24_TYPELESS", - "X32_TYPELESS_G8X24_UINT", - "R10G10B10A2_TYPELESS", - "R10G10B10A2_UNORM", - "R10G10B10A2_UINT", - "R11G11B10_FLOAT", - "R8G8B8A8_TYPELESS", - "R8G8B8A8_UNORM", - "R8G8B8A8_UNORM_SRGB", - "R8G8B8A8_UINT", - "R8G8B8A8_SNORM", - "R8G8B8A8_SINT", - "R16G16_TYPELESS", - "R16G16_FLOAT", - "R16G16_UNORM", - "R16G16_UINT", - "R16G16_SNORM", - "R16G16_SINT", - "R32_TYPELESS", - "D32_FLOAT", - "R32_FLOAT", - "R32_UINT", - "R32_SINT", - "R24G8_TYPELESS", - "D24_UNORM_S8_UINT", - "R24_UNORM_X8_TYPELESS", - "X24_TYPELESS_G8_UINT", - "R8G8_TYPELESS", - "R8G8_UNORM", - "R8G8_UINT", - "R8G8_SNORM", - "R8G8_SINT", - "R16_TYPELESS", - "R16_FLOAT", - "D16_UNORM", - "R16_UNORM", - "R16_UINT", - "R16_SNORM", - "R16_SINT", - "R8_TYPELESS", - "R8_UNORM", - "R8_UINT", - "R8_SNORM", - "R8_SINT", - "A8_UNORM", - "R1_UNORM", - "R9G9B9E5_SHAREDEXP", - "R8G8_B8G8_UNORM", - "G8R8_G8B8_UNORM", - "BC1_TYPELESS", - "BC1_UNORM", - "BC1_UNORM_SRGB", - "BC2_TYPELESS", - "BC2_UNORM", - "BC2_UNORM_SRGB", - "BC3_TYPELESS", - "BC3_UNORM", - "BC3_UNORM_SRGB", - "BC4_TYPELESS", - "BC4_UNORM", - "BC4_SNORM", - "BC5_TYPELESS", - "BC5_UNORM", - "BC5_SNORM", - "B5G6R5_UNORM", - "B5G5R5A1_UNORM", - "B8G8R8A8_UNORM", - "B8G8R8X8_UNORM", - "R10G10B10_XR_BIAS_A2_UNORM", - "B8G8R8A8_TYPELESS", - "B8G8R8A8_UNORM_SRGB", - "B8G8R8X8_TYPELESS", - "B8G8R8X8_UNORM_SRGB", - "BC6H_TYPELESS", - "BC6H_UF16", - "BC6H_SF16", - "BC7_TYPELESS", - "BC7_UNORM", - "BC7_UNORM_SRGB", - "AYUV", - "Y410", - "Y416", - "NV12", - "P010", - "P016", - "420_OPAQUE", - "YUY2", - "Y210", - "Y216", - "NV11", - "AI44", - "IA44", - "P8", - "A8P8", - "B4G4R4A4_UNORM", - "P208", - "V208", - "V408", - "SAMPLER_FEEDBACK_MIN_MIP_OPAQUE,", - "SAMPLER_FEEDBACK_MIP_REGION_USED_OPAQUE,", - "FORCE_UINT", -] -""" -Modern DX10+ formats. - -https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format -""" - -LegacyFormat = Literal[ - "DXT1", - "DXT3", - "DXT5", -] -""" -Legacy DX9 formats. Those are the FourCC of those formats. -""" - -DDSFormat = Union[DxgiFormat, LegacyFormat] - -LEGACY_TO_DXGI: dict[LegacyFormat, DxgiFormat] = { - "DXT1": "BC1_UNORM", - "DXT3": "BC2_UNORM", - "DXT5": "BC3_UNORM", -} - -SRGB_FORMATS: set[DxgiFormat] = { - "BC1_UNORM_SRGB", - "BC2_UNORM_SRGB", - "BC3_UNORM_SRGB", - "BC7_UNORM_SRGB", - "R8G8B8A8_UNORM_SRGB", - "B8G8R8A8_UNORM_SRGB", - "B8G8R8X8_UNORM_SRGB", -} - -WITH_ALPHA: set[DDSFormat] = { - "R32G32B32A32_TYPELESS", - "R32G32B32A32_FLOAT", - "R32G32B32A32_UINT", - "R32G32B32A32_SINT", - "R16G16B16A16_TYPELESS", - "R16G16B16A16_FLOAT", - "R16G16B16A16_UNORM", - "R16G16B16A16_UINT", - "R16G16B16A16_SNORM", - "R16G16B16A16_SINT", - "R10G10B10A2_TYPELESS", - "R10G10B10A2_UNORM", - "R10G10B10A2_UINT", - "R8G8B8A8_TYPELESS", - "R8G8B8A8_UNORM", - "R8G8B8A8_UNORM_SRGB", - "R8G8B8A8_UINT", - "R8G8B8A8_SNORM", - "R8G8B8A8_SINT", - "A8_UNORM", - "BC1_TYPELESS", - "BC1_UNORM", - "BC1_UNORM_SRGB", - "BC2_TYPELESS", - "BC2_UNORM", - "BC2_UNORM_SRGB", - "BC3_TYPELESS", - "BC3_UNORM", - "BC3_UNORM_SRGB", - "B5G5R5A1_UNORM", - "B8G8R8A8_UNORM", - "R10G10B10_XR_BIAS_A2_UNORM", - "B8G8R8A8_TYPELESS", - "B8G8R8A8_UNORM_SRGB", - "BC7_TYPELESS", - "BC7_UNORM", - "BC7_UNORM_SRGB", - "AYUV", - "B4G4R4A4_UNORM", - # legacy - "DXT1", - "DXT3", - "DXT5", -} - -BC7_FORMATS: set[DxgiFormat] = { - "BC7_TYPELESS", - "BC7_UNORM", - "BC7_UNORM_SRGB", -} -BC123_FORMATS: set[DDSFormat] = { - "BC1_TYPELESS", - "BC1_UNORM", - "BC1_UNORM_SRGB", - "BC2_TYPELESS", - "BC2_UNORM", - "BC2_UNORM_SRGB", - "BC3_TYPELESS", - "BC3_UNORM", - "BC3_UNORM_SRGB", - # legacy - "DXT1", - "DXT3", - "DXT5", -} - -PREFER_DX9: set[DDSFormat] = { - "R8G8B8A8_UNORM", - "B8G8R8A8_UNORM", - "B5G5R5A1_UNORM", - "B5G6R5_UNORM", - "B8G8R8X8_UNORM", - "R8G8_UNORM", - "R8_UNORM", -} - - -def to_dxgi(f: DDSFormat) -> DxgiFormat: - if f in LEGACY_TO_DXGI: - return LEGACY_TO_DXGI[f] - return cast(DxgiFormat, f) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/texconv.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/texconv.py deleted file mode 100644 index ae23db7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dds/texconv.py +++ /dev/null @@ -1,150 +0,0 @@ -from __future__ import annotations - -import os -import platform -import shutil -import subprocess -import sys -import uuid -from pathlib import Path -from tempfile import mkdtemp - -import numpy as np -from sanic.log import logger - -from ...utils.utils import split_file_path -from ..image_utils import cv_save_image -from .format import SRGB_FORMATS, DxgiFormat - -__TEXCONV_DIR = os.path.join( - os.path.dirname(sys.modules["__main__"].__file__), # type: ignore - "texconv", # type: ignore -) -__TEXCONV_EXE = os.path.join(__TEXCONV_DIR, "texconv.exe") - - -def __decode(b: bytes) -> str: - try: - return b.decode(encoding="iso8859-1") - except Exception: - try: - return b.decode(encoding="utf-8") - except Exception: - return str(b) - - -def __run_texconv(args: list[str], error_message: str): - if platform.system() != "Windows": - # texconv is only supported on Windows. - raise ValueError( - "Texconv is only supported on Windows." - " Reading and writing DDS files is only partially supported on other systems." - ) - - result = subprocess.run( - [__TEXCONV_EXE, "-nologo", *args], - check=False, - capture_output=True, - ) - - if result.returncode != 0: - output = (__decode(result.stdout) + __decode(result.stderr)).replace("\r", "") - logger.error( - "\n".join( - [ - "Failed to run texconv.", - f"texconv: {__TEXCONV_EXE}", - f"args: {args}", - f"exit code: {result.returncode}", - f"output: {output}", - ] - ) - ) - raise ValueError(f"{error_message}: Code {result.returncode}: {output}") - - -def dds_to_png_texconv(path: Path) -> Path: - """ - Converts the given DDS file to PNG by creating a temporary PNG file. - """ - prefix = uuid.uuid4().hex - _, basename, _ = split_file_path(path) - - tempdir = mkdtemp(prefix="chaiNNer-") - - __run_texconv( - [ - "-f", - "rgba", - "-srgb", - "-ft", - "png", - "-px", - prefix, - "-o", - tempdir, - str(path), - ], - "Unable to convert DDS", - ) - - return (Path(tempdir) / (prefix + basename)).with_suffix(".png") - - -def save_as_dds( - path: Path, - image: np.ndarray, - dds_format: DxgiFormat, - mipmap_levels: int = 0, - uniform_weighting: bool = False, - dithering: bool = False, - minimal_compression: bool = False, - maximum_compression: bool = False, - dx9: bool = False, - separate_alpha: bool = False, -): - """ - Saves an image as DDS using texconv. - See the following page for more information on save options: - https://github.com/Microsoft/DirectXTex/wiki/Texconv - """ - target_dir, name, ext = split_file_path(path) - - assert ext == ".dds", "The file to save must end with '.dds'" - - temp_dir = mkdtemp(prefix="chaiNNer-") - - try: - temp_png = os.path.join(temp_dir, f"{name}.png") - cv_save_image(temp_png, image, []) - - args = [ - "-y", - "-f", - dds_format, - "-dx9" if dx9 else "-dx10", - "-m", - str(mipmap_levels), - # use texconv to directly produce the target file - "-o", - str(target_dir), - ] - - bc = "" - bc += "u" if uniform_weighting else "" - bc += "d" if dithering else "" - bc += "q" if minimal_compression else "" - bc += "x" if maximum_compression else "" - if bc: - args.extend(["-bc", f"-{bc}"]) - - if dds_format in SRGB_FORMATS: - args.append("-srgbi") - - if separate_alpha: - args.append("-sepalpha") - - args.append(temp_png) - __run_texconv(args, "Unable to write DDS") - finally: - shutil.rmtree(temp_dir) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/constants.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/constants.py deleted file mode 100644 index 8131c3c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/constants.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from chainner_ext import DiffusionAlgorithm - - -class ErrorDiffusionMap(Enum): - FLOYD_STEINBERG = "FS" - JARVIS_ET_AL = "JJN" - STUCKI = "ST" - ATKINSON = "A" - BURKES = "B" - SIERRA = "S" - TWO_ROW_SIERRA = "S2" - SIERRA_LITE = "SL" - - -ERROR_PROPAGATION_MAP_LABELS = { - ErrorDiffusionMap.FLOYD_STEINBERG: "Floyd-Steinberg", - ErrorDiffusionMap.JARVIS_ET_AL: "Jarvis, Judice, and Ninke", - ErrorDiffusionMap.STUCKI: "Stucki", - ErrorDiffusionMap.ATKINSON: "Atkinson", - ErrorDiffusionMap.BURKES: "Burkes", - ErrorDiffusionMap.SIERRA: "Sierra", - ErrorDiffusionMap.TWO_ROW_SIERRA: "Two Row Sierra", - ErrorDiffusionMap.SIERRA_LITE: "Sierra Lite", -} - -DIFFUSION_ALGORITHM_MAP: dict[ErrorDiffusionMap, DiffusionAlgorithm] = { - ErrorDiffusionMap.FLOYD_STEINBERG: DiffusionAlgorithm.FloydSteinberg, - ErrorDiffusionMap.JARVIS_ET_AL: DiffusionAlgorithm.JarvisJudiceNinke, - ErrorDiffusionMap.STUCKI: DiffusionAlgorithm.Stucki, - ErrorDiffusionMap.ATKINSON: DiffusionAlgorithm.Atkinson, - ErrorDiffusionMap.BURKES: DiffusionAlgorithm.Burkes, - ErrorDiffusionMap.SIERRA: DiffusionAlgorithm.Sierra, - ErrorDiffusionMap.TWO_ROW_SIERRA: DiffusionAlgorithm.TwoRowSierra, - ErrorDiffusionMap.SIERRA_LITE: DiffusionAlgorithm.SierraLite, -} - - -# https://en.wikipedia.org/wiki/Ordered_dithering - - -class ThresholdMap(Enum): - BAYER_2 = "B2" - BAYER_4 = "B4" - BAYER_8 = "B8" - BAYER_16 = "B16" - - -THRESHOLD_MAP_LABELS = { - ThresholdMap.BAYER_2: "Bayer 2x2", - ThresholdMap.BAYER_4: "Bayer 4x4", - ThresholdMap.BAYER_8: "Bayer 8x8", - ThresholdMap.BAYER_16: "Bayer 16x16", -} diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/palette.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/palette.py deleted file mode 100644 index bcee305..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/dithering/palette.py +++ /dev/null @@ -1,82 +0,0 @@ -import cv2 -import numpy as np - -from ..image_utils import MAX_VALUES_BY_DTYPE, as_3d - - -def _as_float32(image: np.ndarray) -> np.ndarray: - if image.dtype == np.float32: - return image - max_value = MAX_VALUES_BY_DTYPE[image.dtype.name] - return image.astype(np.float32) / max_value - - -def distinct_colors_palette(image: np.ndarray) -> np.ndarray: - image = as_3d(image) - return np.unique(image.reshape((-1, image.shape[2])), axis=0).reshape( - (1, -1, image.shape[2]) - ) - - -def kmeans_palette(image: np.ndarray, num_colors: int) -> np.ndarray: - image = as_3d(image) - flat_image = _as_float32(image.reshape((-1, image.shape[2]))) - - max_iter = 10 - epsilon = 1.0 - criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, max_iter, epsilon) - - attempts = 10 - cv2.setRNGSeed(0) - _, _, center = cv2.kmeans( - flat_image, # type: ignore - num_colors, - None, # type: ignore - criteria, - attempts, - cv2.KMEANS_PP_CENTERS, - ) - - return center.reshape((1, -1, image.shape[2])) - - -class MedianCutBucket: - def __init__(self, data: np.ndarray): - self.data = data - self.n_pixels, self.n_channels = data.shape - self.min_values = np.min(data, axis=0) - self.max_values = np.max(data, axis=0) - self.channel_ranges = self.max_values - self.min_values - self.biggest_range = np.max(self.channel_ranges) - - def split(self): - widest_channel = np.argmax(self.channel_ranges) - median = np.median(self.data[:, widest_channel]) - mask = self.data[:, widest_channel] > median - if mask.sum() == 0: - mean = np.mean(self.data[:, widest_channel]) - mask = self.data[:, widest_channel] > mean - return MedianCutBucket(self.data[mask == True]), MedianCutBucket( - self.data[mask == False] - ) - - def average(self): - return np.mean(self.data, axis=0) - - -def median_cut_palette(image: np.ndarray, num_colors: int) -> np.ndarray: - image = as_3d(image) - flat_image = _as_float32(image.reshape((-1, image.shape[2]))) - - buckets = [MedianCutBucket(flat_image)] - while len(buckets) < num_colors: - bucket_idx, bucket = max(enumerate(buckets), key=lambda x: x[1].biggest_range) - if bucket.biggest_range == 0: - break - buckets.pop(bucket_idx) - - buckets.extend(bucket.split()) - - return np.stack([bucket.average() for bucket in buckets], axis=0).reshape( - (1, -1, image.shape[2]) - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ffmpeg.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ffmpeg.py deleted file mode 100644 index 488b54f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ffmpeg.py +++ /dev/null @@ -1,140 +0,0 @@ -from __future__ import annotations - -import zipfile -from concurrent.futures import Future, ThreadPoolExecutor -from dataclasses import dataclass -from pathlib import Path -from subprocess import check_call - -import requests -from sanic.log import logger - -from system import is_arm_mac, is_linux, is_mac, is_windows - - -def get_download_url() -> str: - if is_windows: - return "https://github.com/chaiNNer-org/ffmpeg-rehost/releases/download/ffmpeg/ffmpeg-win32-x64.zip" - - if is_arm_mac: - return "https://github.com/chaiNNer-org/ffmpeg-rehost/releases/download/ffmpeg/ffmpeg-darwin-arm64.zip" - if is_mac: - return "https://github.com/chaiNNer-org/ffmpeg-rehost/releases/download/ffmpeg/ffmpeg-darwin-x64.zip" - - if is_linux: - return "https://github.com/chaiNNer-org/ffmpeg-rehost/releases/download/ffmpeg/ffmpeg-linux-x64.zip" - - raise Exception("Unsupported platform") - - -def get_executable_path(base_dir: Path) -> tuple[Path, Path]: - if is_windows: - return base_dir / "ffmpeg.exe", base_dir / "ffprobe.exe" - - return base_dir / "ffmpeg", base_dir / "ffprobe" - - -def setup_integrated_ffmpeg(base_dir: Path): - url = get_download_url() - download_path = base_dir / "_ffmpeg.zip" - - logger.info("Downloading FFMPEG...") - with requests.get(url, stream=True) as r: - r.raise_for_status() - base_dir.mkdir(exist_ok=True, parents=True) - with download_path.open("wb") as f: - for chunk in r.iter_content(chunk_size=8192): - f.write(chunk) - - logger.info("Extracting FFMPEG...") - with zipfile.ZipFile(download_path, "r") as zip_ref: - # extract and flatten the zip - for file in zip_ref.filelist: - zip_ref.extract(file, base_dir) - download_path.unlink() - - logger.info("Settings permissions for FFMPEG...") - ffmpeg_path, ffprobe_path = get_executable_path(base_dir) - - if is_mac: - # Un-Quarantine ffmpeg on macOS - try: - check_call(["xattr", "-dr", "com.apple.quarantine", str(ffmpeg_path)]) - check_call(["xattr", "-dr", "com.apple.quarantine", str(ffprobe_path)]) - except Exception as e: - logger.warn(f"Failed to un-quarantine ffmpeg: {e}") - - if is_arm_mac: - # M1 can only run signed files, we must ad-hoc sign it - try: - check_call(["xattr", "-cr", str(ffmpeg_path)]) - check_call(["xattr", "-cr", str(ffprobe_path)]) - check_call(["codesign", "-s", "-", str(ffmpeg_path)]) - check_call(["codesign", "-s", "-", str(ffprobe_path)]) - except Exception as e: - logger.warn(f"Failed to sign ffmpeg: {e}") - - if is_mac or is_linux: - # Make the files executable - try: - ffmpeg_path.chmod(0o7777) - ffprobe_path.chmod(0o7777) - except Exception as e: - logger.warn(f"Failed to set permissions for ffmpeg: {e}") - - -_setup_future: Future[None] | None = None - - -def run_setup(base_dir: Path): - # we want to run setup_integrated_ffmpeg in a new thread, and wait for it to finish. - # we do this so that multiple threads don't try to download and extract the ffmpeg binaries at the same time. - - def task(): - try: - setup_integrated_ffmpeg(base_dir) - return - except Exception as e: - logger.warn(f"Failed to setup FFMPEG: {e}") - logger.warn("Trying again...") - - setup_integrated_ffmpeg(base_dir) - - global _setup_future - if _setup_future is None: - executor = ThreadPoolExecutor(max_workers=1) - _setup_future = executor.submit(task) - executor.shutdown(wait=True) - - _setup_future.result() - - -@dataclass(frozen=True) -class FFMpegEnv: - """ - Paths to FFMPEG binaries. - """ - - ffmpeg: str - ffprobe: str - - @staticmethod - def from_env() -> FFMpegEnv: - return FFMpegEnv( - ffmpeg="ffmpeg", - ffprobe="ffprobe", - ) - - @staticmethod - def get_integrated(base_dir: Path) -> FFMpegEnv: - base_dir = base_dir.resolve() / "ffmpeg" - ffmpeg_path, ffprobe_path = get_executable_path(base_dir) - - if not ffmpeg_path.exists(): - logger.info(f"Integrated FFMPEG not found at {ffmpeg_path}") - run_setup(base_dir) - - return FFMpegEnv( - ffmpeg=str(ffmpeg_path), - ffprobe=str(ffprobe_path), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/gradients.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/gradients.py deleted file mode 100644 index 6e9160b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/gradients.py +++ /dev/null @@ -1,60 +0,0 @@ -import math - -import numpy as np - - -def horizontal_gradient(img: np.ndarray): - x = np.arange(img.shape[1]) - p = x / (img.shape[1] - 1) - img[:, :] = p.reshape((1, -1)) - - -def vertical_gradient(img: np.ndarray): - x = np.arange(img.shape[0]) - p = x / (img.shape[0] - 1) - img[:, :] = p.reshape((-1, 1)) - - -def diagonal_gradient(img: np.ndarray, angle: float, width: float): - center = np.array([img.shape[0], img.shape[1]], dtype=np.float32) / 2 - direction = np.array([np.cos(angle), np.sin(angle)], dtype=np.float32) - - start = center - direction * width / 2 - - pixels = np.array( - [[(r, c) for r in range(img.shape[0]) for c in range(img.shape[1])]] - ) - projection = (pixels - start).dot(direction) - p = np.clip((projection / width).ravel(), 0, 1) - img[:] = p.reshape(img.shape) - - -def radial_gradient( - img: np.ndarray, inner_radius_percent: float = 0, outer_radius_percent: float = 1 -): - inner_radius = inner_radius_percent * img.shape[1] / 2 - outer_radius = outer_radius_percent * img.shape[1] / 2 - - center = np.array(img.shape[:2], dtype="float32") / 2 - pixels = np.array( - [(r, c) for r in range(img.shape[0]) for c in range(img.shape[1])] - ) - distance = np.sqrt(np.sum((pixels - center) ** 2, axis=1)) - p = (distance - inner_radius) / (outer_radius - inner_radius) - img[:] = p.reshape(img.shape) - - -def conic_gradient(img: np.ndarray, rotation: float = 0): - if rotation > np.pi: - rotation -= 2 * np.pi - if rotation < -np.pi: - rotation += 2 * np.pi - - center = (np.array(img.shape[:2], dtype="float32") - 1) / 2 - pixels = np.array( - [(r, c) for r in range(img.shape[0]) for c in range(img.shape[1])] - ) - angles = np.arctan2(pixels[:, 0] - center[0], pixels[:, 1] - center[1]) + rotation - angles[angles < 0] += 2 * np.pi - p = angles / math.pi / 2 - img[:] = p.reshape(img.shape) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/auto_split.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/auto_split.py deleted file mode 100644 index feb0205..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/auto_split.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import annotations - -import gc - -import numpy as np - -try: - from ncnn_vulkan import ncnn - - use_gpu = True -except ImportError: - from ncnn import ncnn # type: ignore - - use_gpu = False -from sanic.log import logger - -from ...utils.utils import get_h_w_c -from ..image_utils import to_uint8 -from ..upscale.auto_split import Split, Tiler, auto_split - - -def ncnn_auto_split( - img: np.ndarray, - net, # noqa: ANN001 - input_name: str, - output_name: str, - blob_vkallocator, # noqa: ANN001 - staging_vkallocator, # noqa: ANN001 - tiler: Tiler, -) -> np.ndarray: - def upscale(img: np.ndarray, _: object): - ex = net.create_extractor() - if use_gpu: - ex.set_blob_vkallocator(blob_vkallocator) - ex.set_workspace_vkallocator(blob_vkallocator) - ex.set_staging_vkallocator(staging_vkallocator) - # ex.set_light_mode(True) - try: - lr_c = get_h_w_c(img)[2] - lr_img_fix = to_uint8(img) - if lr_c == 1: - pixel_type = ncnn.Mat.PixelType.PIXEL_GRAY - elif lr_c == 3: - pixel_type = ncnn.Mat.PixelType.PIXEL_RGB - else: - pixel_type = ncnn.Mat.PixelType.PIXEL_RGBA - mat_in = ncnn.Mat.from_pixels( - lr_img_fix, - pixel_type, - lr_img_fix.shape[1], - lr_img_fix.shape[0], - ) - mean_vals = [] - norm_vals = [1 / 255.0] * lr_c - mat_in.substract_mean_normalize(mean_vals, norm_vals) - ex.input(input_name, mat_in) - _, mat_out = ex.extract(output_name) - result = np.array(mat_out).transpose(1, 2, 0).astype(np.float32) - del ex, mat_in, mat_out - gc.collect() - if use_gpu: - # Clear VRAM - blob_vkallocator.clear() - staging_vkallocator.clear() - return result - except Exception as e: - if "vkQueueSubmit" in str(e): - ex = None - del ex - gc.collect() - if use_gpu: - blob_vkallocator.clear() - staging_vkallocator.clear() - # TODO: Have someone running into this issue enable this and see if it fixes anything - # ncnn.destroy_gpu_instance() - raise RuntimeError( - "A critical error has occurred. You may need to restart chaiNNer in order for NCNN upscaling to start working again." - ) from e - # Check to see if its actually the NCNN out of memory error - if "failed" in str(e): - # clear VRAM - logger.debug("NCNN out of VRAM, clearing VRAM and splitting.") - ex = None - del ex - gc.collect() - if use_gpu: - blob_vkallocator.clear() - staging_vkallocator.clear() - return Split() - else: - # Re-raise the exception if not an OOM error - raise - - return auto_split(img, upscale, tiler) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/model.py deleted file mode 100644 index 5f6c697..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/model.py +++ /dev/null @@ -1,805 +0,0 @@ -from __future__ import annotations - -import os -from copy import deepcopy -from io import BufferedReader, StringIO -from json import load as jload -from pathlib import Path - -import numpy as np -from sanic.log import logger - -from ...utils.checked_cast import checked_cast - -param_schema_file = os.path.join( - os.path.dirname(os.path.realpath(__file__)), "param_schema.json" -) -with open(param_schema_file, encoding="utf-8") as schemaf: - param_schema = jload(schemaf) - -DTYPE_FP32 = b"\x00\x00\x00\x00" -DTYPE_FP16 = b"\x47\x6b\x30\x01" -DTYPE_DICT = {b"\x00\x00\x00\x00": np.float32, b"\x47\x6b\x30\x01": np.float16} - - -class UnaryOpTypes: - ABS = 0 - NEG = 1 - FLOOR = 2 - CEIL = 3 - SQUARE = 4 - SQRT = 5 - RSQ = 6 - EXP = 7 - LOG = 8 - SIN = 9 - COS = 10 - TAN = 11 - ASIN = 12 - ACOS = 13 - ATAN = 14 - RECIPROCAL = 15 - TANH = 16 - - -class BinaryOpTypes: - ADD = 0 - SUB = 1 - MUL = 2 - DIV = 3 - MAX = 4 - MIN = 5 - POW = 6 - RSUB = 7 - RDIV = 8 - - -class CastElementTypes: - AUTO = 0 - FLOAT32 = 1 - FLOAT16 = 2 - INT8 = 3 - BFLOAT16 = 4 - - -class EltwiseOpTypes: - PROD = 0 - SUM = 1 - MAX = 2 - - -class GruDirectionFlags: - FORWARD = 0 - REVERSE = 1 - BIDIRECTIONAL = 2 - - -class InterpResizeTypes: - NEAREST = 1 - BILINEAR = 2 - BICUBIC = 3 - - -class NormalizeEpsModes: - CAFFE = 0 - PYTORCH = 1 - TENSORFLOW = 2 - - -class PaddingTypes: - CONSTANT = 0 - REPLICATE = 1 - REFLECT = 2 - - -class PadModes: - FULL = 0 - VALID = 1 - SAMEUPPER = 2 - SAMELOWER = 3 - - -class PermuteOrderTypes: - WH_WHC_WHDC = 0 - HW_HWC_HWDC = 1 - WCH_WDHC = 2 - CWH_DWHC = 3 - HCW_HDWC = 4 - CHW_DHWC = 5 - WHCD = 6 - HWCD = 7 - WCHD = 8 - CWHD = 9 - HCWD = 10 - CHWD = 11 - WDCH = 12 - DWCH = 13 - WCDH = 14 - CWDH = 15 - DCWH = 16 - CDWH = 17 - HDCW = 18 - DHCW = 19 - HCDW = 20 - CHDW = 21 - DCHW = 22 - CDHW = 23 - - -class ReductionOpTypes: - SUM = 0 - ASUM = 1 - SUMSQ = 2 - MEAN = 3 - MAX = 4 - MIN = 5 - PROD = 6 - L1 = 7 - L2 = 8 - LOGSUM = 9 - LOGSUMEXP = 10 - - -class GridSampleSampleTypes: - NEAREST = 1 - BILINEAR = 2 - BICUBIC = 3 - - -class GridSamplePadModes: - ZEROS = 1 - BORDER = 2 - REFLECTION = 3 - - -class LrnRegionTypes: - ACROSS_CHANNELS = 0 - WITH_CHANNEL = 1 - - -class NcnnWeight: - def __init__(self, weight: np.ndarray, quantize_tag: bytes = b""): - self.quantize_tag = quantize_tag - self.weight = weight - - @property - def shape(self) -> tuple: - return self.weight.shape - - -class NcnnParam: - def __init__( - self, - pid: str, - name: str, - value: float | int | list[float | int], - default: float | int, - ) -> None: - self.id: str = pid - self.name: str = name - self.value: float | int | list[float | int] = value - self.default: float | int = default - - -class NcnnParamCollection: - def __init__( - self, - op: str, - param_dict: dict[int, NcnnParam] | None = None, - ) -> None: - self.op: str = op - self.param_dict: dict[int, NcnnParam] = {} if param_dict is None else param_dict - self.weight_order: dict[str, list[int]] = ( - param_schema[self.op]["weightOrder"] if self.op else {} - ) - - def __getitem__(self, pid: int) -> NcnnParam: - try: - return self.param_dict[pid] - except KeyError as exc: - idstr = str(pid) - param_dict = param_schema[self.op] - try: - param = param_dict[idstr] - except KeyError: - logger.error(f"Op {self.op} does not have param {pid}, please report") - raise - - default_value = param["defaultValue"] - value = param["defaultValue"] - if isinstance(value, str): - for key, val in list(param_dict.items())[:-1]: - if value == val["paramPhase"]: - try: - value = self.param_dict[int(key)].value - except KeyError: - value = val["defaultValue"] - default_value = val["defaultValue"] - - break - else: - msg = f"Op {self.op} does not have param {value}, please report" - raise KeyError(msg) from exc - - return NcnnParam(idstr, param["paramPhase"], value, default_value) - - def __setitem__(self, pid: int, value: float | int | list[float | int]) -> None: - idstr = str(pid) - param_dict = param_schema[self.op] - try: - param = param_dict[idstr] - except KeyError: - logger.error(f"Op {self.op} does not have param {idstr}, please report") - raise - name = param["paramPhase"] - def_val = param["defaultValue"] - - self.param_dict[pid] = NcnnParam(idstr, name, value, def_val) - - def __delitem__(self, key: int) -> None: - try: - del self.param_dict[key] - except KeyError: - pass - - def __contains__(self, item: int) -> bool: - if item in self.param_dict: - return True - return False - - def __str__(self) -> str: - output = "" - param_dict = param_schema[self.op] - self.param_dict = dict(sorted(self.param_dict.items())) - for v in self.param_dict.values(): - if v.value == v.default: - continue - if isinstance(v.default, str) and "FLT_MAX" not in v.default: - pid = None - for key, val in list(param_dict.items())[:-1]: - if v.default == val["paramPhase"]: - pid = int(key) - break - else: - msg = f"Op {self.op} does not have param {v.default}, please report" - raise KeyError(msg) - - # If a param that defaults to the value of another param, if it's value - # equals that of the second param or its default, skip writing it - if v.value in ( - self.param_dict[pid].value, - self.param_dict[pid].default, - ): - continue - - if isinstance(v.value, list): - output += " -233" + v.id.zfill(2) + "=" - else: - output += " " + v.id + "=" - - if isinstance(v.value, float): - v_str = np.format_float_scientific(v.value, 6, False, exp_digits=2) - elif isinstance(v.value, list): - v_str = ",".join( - [ - np.format_float_scientific(n, 6, False, exp_digits=2) - if isinstance(n, float) - else str(n) - for n in v.value - ] - ) - else: - v_str = str(v.value) - - output += v_str - - return output - - def set_op(self, op: str) -> None: - self.op = op - self.weight_order = param_schema[op]["weightOrder"] - - -class NcnnLayer: - def __init__( - self, - op_type: str = "", - name: str = "", - num_inputs: int = 0, - num_outputs: int = 0, - inputs: list[str] | None = None, - outputs: list[str] | None = None, - params: NcnnParamCollection | None = None, - weight_data: dict[str, NcnnWeight] | None = None, - ): - self.op_type: str = op_type - self.name: str = name - self.num_inputs: int = num_inputs - self.num_outputs: int = num_outputs - self.inputs: list[str] = [] if inputs is None else inputs - self.outputs: list[str] = [] if outputs is None else outputs - self.params: NcnnParamCollection = ( - NcnnParamCollection(op_type) if params is None else params - ) - self.weight_data: dict[str, NcnnWeight] = ( - {} if weight_data is None else weight_data - ) - - def add_param(self, pid: int, value: float | int | list[float | int]) -> None: - self.params[pid] = value - - def add_weight( - self, - weight_name: str, - data: float | int | np.ndarray, - quantize_tag: bytes = b"", - ) -> int: - if isinstance(data, float): - data_array = np.array(data, np.float32) - elif isinstance(data, int): - data_array = np.array(data, np.int32) - else: - data_array = data - - if quantize_tag == DTYPE_FP16: - data_array = data_array.astype(np.float16) - else: - # Since int8 not supported, all data that is not fp16 is fp32. - # This covers issues caused by converting fp16 ONNX models. - data_array = data_array.astype(np.float32) - self.weight_data[weight_name] = NcnnWeight(data_array, quantize_tag) - - return len(quantize_tag) + len(data_array.tobytes()) - - -class NcnnModel: - def __init__( - self, - node_count: int = 0, - blob_count: int = 0, - ) -> None: - self.node_count: int = node_count - self.blob_count: int = blob_count - self.layers: list[NcnnLayer] = [] - self.bin_length = 0 - - @property - def magic(self): - return "7767517" - - @staticmethod - def load_from_file(param_path: str = "", bin_path: str = "") -> NcnnModel: - if bin_path == "": - bin_path = param_path.replace(".param", ".bin") - elif param_path == "": - param_path = bin_path.replace(".bin", ".param") - - model = NcnnModel() - with open(param_path, encoding="utf-8") as paramf: - with open(bin_path, "rb") as binf: - paramf.readline() - counts = paramf.readline().strip().split(" ") - model.node_count = int(counts[0]) - model.blob_count = int(counts[1]) - - for line in paramf: - op_type, layer = model.parse_param_layer(line) - layer.weight_data = model.load_layer_weights(binf, op_type, layer) - model.add_layer(layer) - - binf.seek(0, os.SEEK_END) - model.bin_length = binf.tell() - - return model - - @staticmethod - def interp_layers( - a: NcnnLayer, b: NcnnLayer, alpha_a: float - ) -> tuple[NcnnLayer, bytes]: - weights_a = a.weight_data - weights_b = b.weight_data - weights_interp: dict[str, NcnnWeight] = {} - layer_bytes = b"" - - if weights_a: - assert len(weights_a) == len( - weights_b - ), "All corresponding nodes must have same number of weights" - - layer_bytes_list = [] - for weight_name, weight_a in weights_a.items(): - try: - weight_b = weights_b[weight_name] - except KeyError: - logger.error( - f"Weights in node {a.name} and {b.name} do not correspond" - ) - raise - - assert ( - weight_a.shape == weight_b.shape - ), "Corresponding weights must have the same size and shape" - - assert len(weight_a.quantize_tag) == len( - weight_b.quantize_tag - ), "Weights must either both have or both not have a quantize tag" - - if ( - weight_a.quantize_tag == DTYPE_FP16 - and weight_b.quantize_tag == DTYPE_FP32 - ): - weight_b.quantize_tag = DTYPE_FP16 - weight_b.weight = weight_b.weight.astype(np.float16) - elif ( - weight_a.quantize_tag == DTYPE_FP32 - and weight_b.quantize_tag == DTYPE_FP16 - ): - weight_a.quantize_tag = DTYPE_FP16 - weight_a.weight = weight_a.weight.astype(np.float16) - - weight_c = NcnnWeight( - (weight_a.weight * alpha_a + weight_b.weight * (1 - alpha_a)), - weight_a.quantize_tag, - ) - layer_bytes_list.append( - weight_c.quantize_tag + weight_c.weight.tobytes() - ) - - weights_interp[weight_name] = weight_c - - layer_bytes = b"".join(layer_bytes_list) - - return ( - NcnnLayer( - a.op_type, - a.name, - a.num_inputs, - a.num_outputs, - a.inputs, - a.outputs, - a.params, - weights_interp, - ), - layer_bytes, - ) - - def add_layer(self, layer: NcnnLayer) -> None: - self.layers.append(layer) - - def parse_param_layer(self, layer_str: str) -> tuple[str, NcnnLayer]: - param_list = layer_str.strip().split() - op_type, name = param_list[:2] - assert op_type != "MemoryData", "This NCNN param file contains invalid layers" - - num_inputs = int(param_list[2]) - num_outputs = int(param_list[3]) - input_end = 4 + num_inputs - output_end = input_end + num_outputs - inputs = list(param_list[4:input_end]) - outputs = list(param_list[input_end:output_end]) - - params = param_list[output_end:] - param_dict = {} - for param_str in params: - ks, vs = param_str.split("=") - k = int(ks) - if k < 0: - v = [] - for vi in vs.split(","): - vi = float(vi) if "." in vi or "e" in vi else int(vi) # noqa: PLW2901 - v.append(vi) - k = abs(k + 23300) - ks = str(k) - elif "." in vs or "e" in vs: - v = float(vs) - else: - v = int(vs) - - param = NcnnParam( - ks, - param_schema[op_type][ks]["paramPhase"], - v, - param_schema[op_type][ks]["defaultValue"], - ) - param_dict[k] = param - - return op_type, NcnnLayer( - op_type, - name, - num_inputs, - num_outputs, - inputs, - outputs, - NcnnParamCollection(op_type, param_dict), - ) - - def load_layer_weights( - self, binf: BufferedReader, op_type: str, layer: NcnnLayer - ) -> dict[str, NcnnWeight]: - weight_dict = {} - if op_type == "BatchNorm": - channels_data = checked_cast(int, layer.params[0].value) * 4 - slope = np.frombuffer(binf.read(channels_data), np.float32) - weight_dict["slope"] = NcnnWeight(slope) - mean = np.frombuffer(binf.read(channels_data), np.float32) - weight_dict["mean"] = NcnnWeight(mean) - variance = np.frombuffer(binf.read(channels_data), np.float32) - weight_dict["variance"] = NcnnWeight(variance) - bias = np.frombuffer(binf.read(channels_data), np.float32) - weight_dict["bias"] = NcnnWeight(bias) - elif op_type in ("Convolution", "ConvolutionDepthWise"): - quantize_tag = binf.read(4) - dtype = DTYPE_DICT[quantize_tag] - weight_data_length = checked_cast(int, layer.params[6].value) - weight_data_size = ( - weight_data_length * 2 - if quantize_tag == DTYPE_FP16 - else weight_data_length * 4 - ) - - has_bias = layer.params[5].value - - num_filters = checked_cast(int, layer.params[0].value) - kernel_w = checked_cast(int, layer.params[1].value) - kernel_h = checked_cast(int, layer.params[11].value) - if op_type == "ConvolutionDepthWise": - group = checked_cast(int, layer.params[7].value) - num_input = ( - weight_data_length // (num_filters // group) // kernel_w // kernel_h - ) - shape = ( - group, - num_filters // group, - num_input // group, - kernel_h, - kernel_w, - ) - else: - num_input = weight_data_length // num_filters // kernel_w // kernel_h - shape = (num_filters, num_input, kernel_h, kernel_w) - - weight_data = np.frombuffer(binf.read(weight_data_size), dtype) - weight_data = weight_data.reshape(shape) - weight_dict["weight"] = NcnnWeight(weight_data, quantize_tag) - - if has_bias: - bias_data_size = num_filters * 4 - bias_data = np.frombuffer(binf.read(bias_data_size), np.float32) - weight_dict["bias"] = NcnnWeight(bias_data) - elif op_type == "Deconvolution": - quantize_tag = binf.read(4) - dtype = DTYPE_DICT[quantize_tag] - weight_data_length = checked_cast(int, layer.params[6].value) - weight_data_size = ( - weight_data_length * 2 - if quantize_tag == DTYPE_FP16 - else weight_data_length * 4 - ) - - has_bias = layer.params[5].value - - num_filters = checked_cast(int, layer.params[0].value) - kernel_w = checked_cast(int, layer.params[1].value) - kernel_h = checked_cast(int, layer.params[11].value) - num_input = weight_data_length // num_filters // kernel_w // kernel_h - shape = (num_filters, num_input, kernel_h, kernel_w) - - weight_data = np.frombuffer(binf.read(weight_data_size), dtype) - weight_data = weight_data.reshape(shape) - weight_dict["weight"] = NcnnWeight(weight_data, quantize_tag) - - if has_bias: - bias_data_size = num_filters * 4 - bias_data = np.frombuffer(binf.read(bias_data_size), np.float32) - weight_dict["bias"] = NcnnWeight(bias_data) - elif op_type == "InnerProduct": - quantize_tag = binf.read(4) - dtype = DTYPE_DICT[quantize_tag] - weight_data_length = layer.params[2].value - assert isinstance(weight_data_length, int), "Weight data size must be int" - weight_data_size = ( - weight_data_length * 2 - if quantize_tag == DTYPE_FP16 - else weight_data_length * 4 - ) - weight_data = np.frombuffer(binf.read(weight_data_size), dtype) - num_output = layer.params[0].value - assert isinstance(num_output, int), "Num output must be int" - num_input = weight_data_length // num_output - weight_data = weight_data.reshape((num_input, num_output)) - weight_dict["weight"] = NcnnWeight(weight_data, quantize_tag) - - has_bias = layer.params[1].value - if has_bias == 1: - bias_data_size = num_output * 4 - bias_data = np.frombuffer(binf.read(bias_data_size), np.float32) - weight_dict["bias"] = NcnnWeight(bias_data) - elif op_type == "PReLU": - num_slope = layer.params[0].value - assert isinstance(num_slope, int), "Num slopes must be int" - slope_data_size = num_slope * 4 - slope_data = np.frombuffer(binf.read(slope_data_size), np.float32) - weight_dict["slope"] = NcnnWeight(slope_data) - elif op_type == "Scale": - scale_data_length = layer.params[0].value - assert isinstance(scale_data_length, int), "Scale data size must be int" - if scale_data_length != -233: - quantize_tag = binf.read(4) - dtype = DTYPE_DICT[quantize_tag] - scale_data_size = ( - scale_data_length * 2 - if quantize_tag == DTYPE_FP16 - else scale_data_length * 4 - ) - scale_data = np.frombuffer(binf.read(scale_data_size), dtype) - weight_dict["weight"] = NcnnWeight(scale_data, quantize_tag) - - has_bias = layer.params[1].value - if has_bias == 1: - bias_data = np.frombuffer( - binf.read(scale_data_length * 4), np.float32 - ) - weight_dict["bias"] = NcnnWeight(bias_data) - elif len(layer.params.weight_order) != 0: - error_msg = f"Load weights not added for {op_type} yet, please report" - raise ValueError(error_msg) - - return weight_dict - - def write_param(self, filename: Path | str = "") -> str: - with StringIO() as p: - p.write(f"{self.magic}\n{self.node_count} {self.blob_count}\n") - - for layer in self.layers: - if layer.op_type == "ncnnfused": - continue - - p.write( - f"{layer.op_type:<16}" - f" {layer.name:<24}" - f" {layer.num_inputs}" - f" {layer.num_outputs}" - ) - if layer.inputs: - p.write(f" {' '.join(layer.inputs)}") - if layer.outputs: - p.write(f" {' '.join(layer.outputs)}") - if layer.params.param_dict: - param_str = str(layer.params) - if param_str: - p.write(f"{param_str}") - p.write("\n") - - if filename: - with open(filename, "w", encoding="utf-8") as f: - f.write(p.getvalue()) - return "" - else: - return p.getvalue() - - def serialize_weights(self) -> bytes: - layer_weights = [ - b"".join((w.quantize_tag, np.ndarray.tobytes(w.weight))) - for l in self.layers - for w in l.weight_data.values() - if l.weight_data and l.op_type != "ncnnfused" - ] - - return b"".join(layer_weights) - - def write_bin(self, filename: Path | str) -> None: - with open(filename, "wb") as f: - f.write(self.serialize_weights()) - - def interpolate(self, model_b: NcnnModel, alpha: float) -> NcnnModel: - interp_model = deepcopy(self) - - layer_a_weights = [(i, l) for i, l in enumerate(self.layers) if l.weight_data] - layer_b_weights = [ - (i, l) for i, l in enumerate(model_b.layers) if l.weight_data - ] - - assert len(layer_a_weights) == len( - layer_b_weights - ), "Models must have same number of layers containing weights" - - weight_bytes_list = [] - for layer_a, layer_b in zip(layer_a_weights, layer_b_weights): - interp_layer, layer_bytes = NcnnModel.interp_layers( - layer_a[1], layer_b[1], alpha - ) - interp_model.layers[layer_a[0]] = interp_layer - weight_bytes_list.append(layer_bytes) - - return interp_model - - @property - def bin(self) -> bytes: - return self.serialize_weights() - - -class NcnnModelWrapper: - def __init__(self, model: NcnnModel) -> None: - self.model: NcnnModel = model - scale, in_nc, out_nc, nf, fp = NcnnModelWrapper.get_broadcast_data(model) - self.scale: int = scale - self.nf: int = nf - self.in_nc: int = in_nc - self.out_nc: int = out_nc - self.fp: str = fp - - @staticmethod - def get_broadcast_data(model: NcnnModel) -> tuple[int, int, int, int, str]: - scale = 1.0 - in_nc = 0 - out_nc = 0 - nf = 0 - fp = "fp32" - pixel_shuffle = 1 - found_first_conv = False - current_conv = None - - for i, layer in enumerate(model.layers): - if layer.op_type == "Interp": - try: - if ( - model.layers[i + 1].op_type != "BinaryOp" - and model.layers[i + 1].params[0].value != 0 - ): - scale *= checked_cast(float, layer.params[1].value) - except IndexError: - scale *= checked_cast(float, layer.params[1].value) - elif layer.op_type == "PixelShuffle": - scale *= checked_cast(int, layer.params[0].value) - pixel_shuffle *= checked_cast(int, layer.params[0].value) - elif layer.op_type in ( - "Convolution", - "Convolution1D", - "ConvolutionDepthWise", - ): - if found_first_conv is not True: - nf, in_nc = NcnnModelWrapper.get_nf_and_in_nc(layer) - if layer.weight_data["weight"].quantize_tag == DTYPE_FP16: - fp = "fp16" - found_first_conv = True - - scale /= checked_cast(int, layer.params[3].value) - current_conv = layer - elif layer.op_type in ("Deconvolution", "DeconvolutionDepthWise"): - if found_first_conv is not True: - nf, in_nc = NcnnModelWrapper.get_nf_and_in_nc(layer) - found_first_conv = True - - scale *= checked_cast(int, layer.params[3].value) - current_conv = layer - - assert ( - current_conv is not None - ), "Cannot broadcast; model has no Convolution layers" - - out_nc = checked_cast(int, current_conv.params[0].value) // pixel_shuffle**2 - - assert scale >= 1, "Models with scale less than 1x not supported" - assert scale % 1 == 0, f"Model not supported, scale {scale} is not an integer" - - return int(scale), in_nc, out_nc, nf, fp - - @staticmethod - def get_nf_and_in_nc(layer: NcnnLayer) -> tuple[int, int]: - nf = layer.params[0].value - kernel_w = layer.params[1].value - try: - kernel_h = layer.params[11].value - except KeyError: - kernel_h = kernel_w - weight_data_size = layer.params[6].value - - assert ( - isinstance(nf, int) - and isinstance(kernel_w, int) - and isinstance(kernel_h, int) - and isinstance(weight_data_size, int) - ), "Out nc, kernel width and height, and weight data size must all be ints" - in_nc = weight_data_size // nf // kernel_w // kernel_h - - return nf, in_nc diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/optimizer.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/optimizer.py deleted file mode 100644 index 07db838..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/optimizer.py +++ /dev/null @@ -1,1208 +0,0 @@ -import numpy as np - -from ...utils.checked_cast import checked_cast -from .model import BinaryOpTypes as BOT # noqa -from .model import EltwiseOpTypes as EOT # noqa -from .model import NcnnLayer, NcnnModel - - -class NcnnOptimizer: - def __init__(self, model: NcnnModel) -> None: - self.model = model - - def __fuse_batchnorm_scale(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "BatchNorm": - # BatchNorm - Scale - batchnorm_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Scale": - continue - if len(self.model.layers[j].inputs) != 1: - continue - if self.model.layers[j].inputs[0] == batchnorm_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse BatchNorm - Scale to BatchNorm - scale = self.model.layers[j] - - bias = layer.weight_data["bias"].weight - - layer.weight_data["slope"].weight = ( - layer.weight_data["slope"].weight - * scale.weight_data["scale"].weight - ) - - bias = bias * scale.weight_data["scale"].weight - if scale.params[1].value: - bias += scale.weight_data["bias"].weight - layer.weight_data["bias"].weight = bias - - layer.outputs[0] = scale.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - scale.op_type = "ncnnfused" - - def __fuse_x_batchnorm(self): - """Combines fuse_convolution_batchnorm, fuse_convolutiondepthwise_batchnorm, - fuse_deconvolution_batchnorm, fuse_deconvolutiondepthwise_batchnorm, and - fuse_innerproduct_batchnorm""" - - for i, layer in enumerate(self.model.layers): - if layer.op_type in ( - "Convolution", - "ConvolutionDepthWise", - "Deconvolution", - "DeconvolutionDepthWise", - "InnerProduct", - ): - # Convolution - BatchNorm - conv_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "BatchNorm": - continue - if len(self.model.layers[j].inputs) != 1: - continue - if self.model.layers[j].inputs[0] == conv_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse Convolution - BatchNorm to Convolution - batchnorm = self.model.layers[j] - - channels = checked_cast(int, batchnorm.params[0].value) - eps = checked_cast(float, batchnorm.params[1].value) - - # a = bias - slope * mean / sqrt(var + eps) - # b = slope / sqrt(var + eps) - # value = value * b + a - a = np.ndarray((channels,)) - b = np.ndarray((channels,)) - sqrt_var = np.sqrt(batchnorm.weight_data["variance"].weight + eps) - a = ( - batchnorm.weight_data["bias"].weight - - batchnorm.weight_data["slope"].weight - * batchnorm.weight_data["mean"].weight - / sqrt_var - ) - b = batchnorm.weight_data["slope"].weight / sqrt_var - - bias_term = 1 if layer.op_type == "InnerProduct" else 5 - - if layer.params[bias_term].value == 0: - # init bias as zero - layer.params[bias_term] = 1 - layer.add_weight("bias", np.zeros(channels, dtype=np.float32)) - - weight = layer.weight_data["weight"].weight - layer.weight_data["weight"].weight = weight * ( - np.transpose( - np.broadcast_to(b, weight.shape[::-1]).astype(weight.dtype), - (3, 2, 1, 0), - ) - ) - - layer.weight_data["bias"].weight = ( - layer.weight_data["bias"].weight * b + a - ) - - self.model.layers[i].outputs[0] = self.model.layers[j].outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - batchnorm.op_type = "ncnnfused" - - def __fuse_x_mul(self): - """Combines fuse_convolution_mul, fuse_convolutiondepthwise_mul, - and fuse_deconvolution_mul""" - - for i, layer in enumerate(self.model.layers): - if layer.op_type in ( - "Convolution", - "ConvolutionDepthWise", - "Deconvolution", - ): - # Convolution - BinaryOp - output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "BinaryOp": - continue - if self.model.layers[j].num_inputs != 2: - continue - if self.model.layers[j].inputs[0] == output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse Convolution - BinaryOp to Convolution - binaryop = self.model.layers[j] - - if binaryop.params[0].value != BOT.MUL or binaryop.params[1].value: - continue - - # MemoryData - ..... - BinaryOp - k = 0 - for k in range(j): - if self.model.layers[k].op_type != "MemoryData": - continue - if self.model.layers[k].outputs[0] == binaryop.inputs[1]: - break - else: - k += 1 - - if k == j: - continue - - memorydata = self.model.layers[k] - - channels = checked_cast(int, layer.params[0].value) - - if ( - memorydata.params[0].value != channels - or memorydata.params[1].value != 0 - or memorydata.params[2].value != 0 - ): - # not bias-like broadcasting type - continue - - data = memorydata.weight_data["data"].weight - - weight = layer.weight_data["weight"].weight - layer.weight_data["weight"].weight = weight * ( - np.transpose( - np.broadcast_to(data, weight.shape[::-1]).astype(weight.dtype), - (3, 2, 1, 0), - ) - ) - - try: - layer.weight_data["bias"].weight = ( - layer.weight_data["bias"].weight * data - ) - except KeyError: - pass - - self.model.layers[i].outputs[0] = self.model.layers[j].outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - binaryop.op_type = "ncnnfused" - - def __fuse_x_add(self): - """Combines fuse_convolution_add, fuse_convolutiondepthwise_add, - fuse_deconvolution_add, and fuse_innerproduct_add""" - - for i, layer in enumerate(self.model.layers): - if layer.op_type in ( - "Convolution", - "ConvolutionDepthWise", - "Deconvolution", - "InnerProduct", - ): - # Convolution - Add - output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "BinaryOp": - continue - if self.model.layers[j].num_inputs != 2: - continue - if self.model.layers[j].inputs[0] == output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse Convolution - BinaryOp to Convolution - binaryop = self.model.layers[j] - - if binaryop.params[0].value != BOT.ADD or binaryop.params[1].value: - continue - - # MemoryData - ..... - BinaryOp - k = 0 - for k in range(j): - if self.model.layers[k].op_type != "MemoryData": - continue - if self.model.layers[k].outputs[0] == binaryop.inputs[1]: - break - else: - k += 1 - - if k == j: - continue - - memorydata = self.model.layers[k] - - channels = checked_cast(int, layer.params[0].value) - - if not ( - memorydata.params[0].value == channels - and memorydata.params[1].value == 0 - and memorydata.params[2].value == 0 - ) or ( - memorydata.params[0].value == 1 - and memorydata.params[1].value == 1 - and memorydata.params[2].value == channels - ): - # not bias-like broadcasting type - continue - - bias_term = 1 if layer.op_type == "InnerProduct" else 5 - bias_data = memorydata.weight_data["data"].weight.reshape(channels) - - if layer.params[bias_term].value == 0: - # init bias - layer.params[bias_term] = 1 - layer.add_weight("bias", bias_data) - else: - layer.weight_data["bias"].weight = ( - layer.weight_data["bias"].weight + bias_data - ) - - self.model.layers[i].outputs[0] = self.model.layers[j].outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - binaryop.op_type = "ncnnfused" - - def __fuse_innerproduct_dropout(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "InnerProduct": - # InnerProduct - Dropout - output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Dropout": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse InnerProduct - Dropout to InnerProduct - dropout = self.model.layers[j] - - scale = checked_cast(float, dropout.params[0].value) - if scale != 1: - layer.weight_data["weight"].weight = ( - layer.weight_data["weight"].weight * scale - ) - - if layer.params[1].value == 1: - layer.weight_data["bias"].weight = ( - layer.weight_data["bias"].weight * scale - ) - - self.model.layers[i].outputs[0] = self.model.layers[j].outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - dropout.op_type = "ncnnfused" - - def __fuse_x_activation(self): - """Combines fuse_convolution_activation, fuse_convolution1d_activation, - fuse_convolutiondepthwise_activation, fuse_deconvolution_activation, - fuse_deconvolutiondepthwise_activation, and fuse_innerproduct_activation""" - - for i, layer in enumerate(self.model.layers): - if layer.op_type in ( - "Convolution", - "Convolution1D", - "ConvolutionDepthWise", - "Deconvolution", - "DeconvolutionDepthWise", - "InnerProduct", - ): - # Convolution - Activation - output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type not in ( - "ReLU", - "Clip", - "Sigmoid", - "Mish", - "Hardswish", - ): - continue - if ( - self.model.layers[j].op_type == "Mish" - and layer.op_type in ("Deconvolution", "DeconvolutionDepthWise") - ) or ( - self.model.layers[j].op_type == "HardSwish" - and layer.op_type - in ( - "Convolution1D", - "Deconvolution", - "DeconvolutionDepthWise", - ) - ): - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse Convolution - Activation to Convolution - act = self.model.layers[j] - - if act.op_type == "ReLU": - if act.params[0].value == 0: - layer.params[9] = 1 - else: - layer.params[9] = 2 - layer.params[10] = [1, checked_cast(float, act.params[0].value)] - elif act.op_type == "Clip": - layer.params[9] = 3 - layer.params[10] = [ - 2, - checked_cast(float, act.params[0].value), - checked_cast(float, act.params[1].value), - ] - elif act.op_type == "Sigmoid": - layer.params[9] = 4 - elif act.op_type == "Mish": - layer.params[9] = 5 - elif act.op_type == "HardSwish": - layer.params[9] = 6 - layer.params[10] = [ - 2, - checked_cast(float, act.params[0].value), - checked_cast(float, act.params[1].value), - ] - - self.model.layers[i].outputs[0] = self.model.layers[j].outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - act.op_type = "ncnnfused" - - def __fuse_memorydata_binaryop(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "MemoryData": - # MemoryData - BinaryOp - output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "BinaryOp": - continue - if self.model.layers[j].num_inputs != 2: - continue - if output in ( - self.model.layers[j].inputs[0], - self.model.layers[j].inputs[1], - ): - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - # fuse MemoryData - BinaryOp to BinaryOp - binaryop = self.model.layers[j] - - if ( - layer.params[0].value != 1 - or layer.params[1].value != 0 - or layer.params[2].value != 0 - ): - # not a scalar - continue - - memorydata_index = 1 - if binaryop.inputs[0] == output: - op_type = checked_cast(int, binaryop.params[0].value) - if op_type == BOT.ADD: - memorydata_index = 0 - elif op_type == BOT.SUB: - binaryop.params[0] = BOT.RSUB - memorydata_index = 0 - elif op_type == BOT.DIV: - binaryop.params[0] = BOT.RDIV - memorydata_index = 0 - else: - # non-interchangeable binaryop - continue - - binaryop.params[1] = 1 - binaryop.params[2] = layer.weight_data["data"].weight[0] - - binaryop.inputs.pop(memorydata_index) - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - i = 0 - while i in range(len(self.model.layers)): - if self.model.layers[i].op_type != "MemoryData": - # MemoryData - Split - BinaryOp - output = self.model.layers[i].outputs[0] - - j0 = i - for j0 in range(i + 1, len(self.model.layers)): - if self.model.layers[j0].op_type != "Split": - continue - if self.model.layers[j0].num_inputs != 1: - continue - if self.model.layers[j0].inputs[0] == output: - break - else: - j0 += 1 - - if j0 == len(self.model.layers): - i += 1 - continue - - split_output_index = -1 - j1 = i - for j1 in range(i + 1, len(self.model.layers)): - if self.model.layers[j1].op_type != "BinaryOp": - continue - if self.model.layers[j1].num_inputs != 2: - continue - for k in range(self.model.layers[j0].num_outputs): - if ( - self.model.layers[j1].inputs[0] - == self.model.layers[j0].outputs[k] - or self.model.layers[j1].inputs[1] - == self.model.layers[j0].outputs[k] - ): - split_output_index = k - break - if split_output_index != -1: - break - else: - j1 += 1 - - if j1 == len(self.model.layers): - i += 1 - continue - - # fuse MemoryData - Split - BinaryOp to BinaryOp - split = self.model.layers[j0] - binaryop = self.model.layers[j1] - - if ( - self.model.layers[i].params[0].value != 1 - or self.model.layers[i].params[1].value != 0 - or self.model.layers[i].params[2].value != 0 - ): - # not a scalar - i += 1 - continue - - memorydata_index = 1 - if binaryop.inputs[0] == split.outputs[split_output_index]: - op_type = checked_cast(int, binaryop.params[0].value) - if op_type in (BOT.ADD, BOT.MUL, BOT.MAX, BOT.MIN): - memorydata_index = 0 - elif op_type == BOT.SUB: - binaryop.params[0] = BOT.RSUB - memorydata_index = 0 - elif op_type == BOT.DIV: - binaryop.params[0] = BOT.RDIV - memorydata_index = 0 - else: - # non-interchangeable binaryop - i += 1 - continue - - binaryop.params[1] = 1 - binaryop.params[2] = self.model.layers[i].weight_data["data"].weight[0] - - binaryop.inputs.pop(memorydata_index) - binaryop.num_inputs -= 1 - split.outputs.pop(split_output_index) - split.num_outputs -= 1 - if split.num_outputs == 0: - self.model.node_count -= 2 - self.model.blob_count -= 2 - split.op_type = "ncnnfused" - self.model.layers[i].op_type = "ncnnfused" - - i -= 1 - - i += 1 - - def __fuse_binaryop_eltwise(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "BinaryOp": - if layer.num_inputs != 2: - continue - if layer.params[0].value != BOT.ADD or layer.params[1].value: - continue - - # BinaryOp - BinaryOp - BinaryOp - input0 = layer.inputs[0] - input1 = layer.inputs[1] - - j0 = 0 - for j0 in range(i): - if self.model.layers[j0].op_type != "BinaryOp": - continue - if self.model.layers[j0].num_inputs != 1: - continue - if self.model.layers[j0].params[0].value != BOT.MUL: - continue - if self.model.layers[j0].outputs[0] == input0: - break - else: - j0 += 1 - - j1 = 0 - for j1 in range(i): - if self.model.layers[j1].op_type != "BinaryOp": - continue - if self.model.layers[j1].num_inputs != 1: - continue - if self.model.layers[j1].params[0].value != BOT.MUL: - continue - if self.model.layers[j1].outputs[0] == input1: - break - else: - j1 += 1 - - if j0 == i and j1 == i: - continue - - binaryop0 = self.model.layers[j0] - binaryop1 = self.model.layers[j1] - - eltwise = NcnnLayer( - "Eltwise", - layer.name, - layer.num_inputs, - layer.num_outputs, - layer.inputs, - layer.outputs, - ) - eltwise.add_param(0, EOT.SUM) - if i not in (j0, j1): - # fuse BinaryOp - BinaryOp - BinaryOp to Eltwise - eltwise.add_param( - 1, - [ - 2, - checked_cast(float, binaryop0.params[2].value), - checked_cast(float, binaryop1.params[2].value), - ], - ) - eltwise.inputs[0] = binaryop0.inputs[0] - eltwise.inputs[1] = binaryop1.inputs[0] - self.model.node_count -= 2 - self.model.blob_count -= 2 - binaryop0.op_type = "ncnnfused" - binaryop1.op_type = "ncnnfused" - elif j0 != i and j1 == i: - # fuse BinaryOp - X - BinaryOp to Eltwise - eltwise.add_param( - 1, [2, checked_cast(float, binaryop0.params[2].value), 1.0] - ) - eltwise.inputs[0] = binaryop0.inputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - binaryop0.op_type = "ncnnfused" - else: - # fuse X - BinaryOp - BinaryOp to Eltwise - eltwise.add_param( - 1, [2, 1.0, checked_cast(float, binaryop1.params[2].value)] - ) - eltwise.inputs[1] = binaryop1.inputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - binaryop1.op_type = "ncnnfused" - - self.model.layers[i] = eltwise - - def __eliminate_dropout(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Dropout": - if layer.params[0].value != 1: - continue - - # Any - Dropout - dropout_input = layer.inputs[0] - - j = i - 1 - for j in range(i - 1, -1, -1): - if self.model.layers[j].op_type == "ncnnfused": - continue - if self.model.layers[j].num_outputs != 1: - continue - if self.model.layers[j].outputs[0] == dropout_input: - break - else: - j -= 1 - - if j == -1: - continue - - self.model.layers[j].outputs[0] = layer.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __eliminate_pooling1x1(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Pooling": - if ( - layer.params[3].value != 0 - or layer.params[13].value != 0 - or layer.params[14].value != 0 - or layer.params[15].value != 0 - ): - continue - if ( - layer.params[1].value != 1 - or layer.params[11].value != 1 - or layer.params[2].value != 1 - or layer.params[12].value != 1 - ): - continue - if layer.params[4].value != 0: - continue - - # Any - Pooling - pooling_input = layer.inputs[0] - - top_i = -1 - j = i - 1 - for j in range(i - 1, -1, -1): - if self.model.layers[j].op_type == "ncnnfused": - continue - - for k in range(self.model.layers[j].num_outputs): - if self.model.layers[j].outputs[k] == pooling_input: - top_i = k - break - - if top_i != -1: - break - else: - j -= 1 - - if j == -1: - continue - - self.model.layers[j].outputs[top_i] = layer.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __eliminate_noop(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Noop": - if layer.num_inputs == 0: - # Noop - layer.op_type = "ncnnfused" - continue - - # Any - Noop - noop_input = layer.inputs[0] - - j = i - 1 - any_k = -1 - for j in range(i - 1, -1, -1): - if self.model.layers[j].op_type == "ncnnfused": - continue - - link_noop = False - for k in range(self.model.layers[j].num_outputs): - if self.model.layers[j].outputs[k] == noop_input: - link_noop = True - any_k = k - break - - if link_noop: - break - else: - j -= 1 - - if j == -1 or any_k == -1: - continue - - self.model.layers[j].outputs[any_k] = layer.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __eliminate_split(self): - blob_input_references = [] - for _, layer in enumerate(self.model.layers): - for input_name in layer.inputs: - blob_input_references.append(input_name) - - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Split": - real_split_output_count = 0 - real_split_output_index = -1 - for j in range(layer.num_outputs): - if layer.outputs[j] in blob_input_references: - real_split_output_count += 1 - real_split_output_index = j - - if real_split_output_count > 1: - continue - - # Any - Pooling - split_input = layer.inputs[0] - - top_i = -1 - j = i - 1 - for j in range(i - 1, -1, -1): - if self.model.layers[j].op_type == "ncnnfused": - continue - - for k in range(self.model.layers[j].num_outputs): - if self.model.layers[j].outputs[k] == split_input: - top_i = k - break - - if top_i != -1: - break - else: - j -= 1 - - if j == -1: - continue - - self.model.layers[j].outputs[top_i] = layer.outputs[ - real_split_output_index - ] - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __eliminate_orphaned_memorydata(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "MemoryData": - # MemoryData - X - memdata_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type == "ncnnfused": - continue - - orphaned = True - for k in range(self.model.layers[j].num_inputs): - if self.model.layers[j].inputs[k] == memdata_output: - orphaned = False - break - - if not orphaned: - break - - if j < len(self.model.layers): - continue - - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __eliminate_reshape_after_global_pooling(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Pooling": - if layer.params[4].value == 0: - continue - - # Pooling - Reshape - pooling_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Reshape": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == pooling_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - reshape = self.model.layers[j] - - if ( - reshape.params[1].value != -233 - or reshape.params[2].value != -233 - or reshape.params[3].value != 0 - ): - continue - - layer.outputs[0] = reshape.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - reshape.op_type = "ncnnfused" - - def __eliminate_flatten_after_global_pooling(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Pooling": - if layer.params[4].value == 0: - continue - - # Pooling - Flatten - pooling_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Flatten": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == pooling_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - flatten = self.model.layers[j] - - layer.outputs[0] = flatten.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - flatten.op_type = "ncnnfused" - - def __eliminate_flatten_after_innerproduct(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "InnerProduct": - # InnerProduct - Flatten - inprod_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Flatten": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == inprod_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - flatten = self.model.layers[j] - - layer.outputs[0] = flatten.outputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - flatten.op_type = "ncnnfused" - - def __eliminate_reshape_before_binaryop(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Reshape": - if ( - layer.params[0].value != 1 - or layer.params[1].value != 1 - or layer.params[3].value != 1 - ): - continue - - # Reshape - BinaryOp - reshape_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "BinaryOp": - continue - if self.model.layers[j].num_inputs != 2: - continue - if reshape_output in ( - self.model.layers[j].inputs[0], - self.model.layers[j].inputs[1], - ): - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - binaryop = self.model.layers[j] - - input_blob_final = layer.inputs[0] - if binaryop.inputs[0] == reshape_output: - binaryop.inputs[0] = input_blob_final - if binaryop.inputs[1] == reshape_output: - binaryop.inputs[1] = input_blob_final - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __replace_reduction_with_global_pooling(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Reduction": - if ( - layer.params[0].value != 3 - or layer.params[1].value != 0 - or layer.params[2].value != 1 - ): - continue - - axes = checked_cast(list, layer.params[3].value) - if len(axes) != 1: - continue - if axes[0] != 2 and axes[0] != 3: - continue - - # Reduction(2/3) - Reduction(2) - reduction1_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Reduction": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == reduction1_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - reduction2 = self.model.layers[j] - - if ( - reduction2.params[0].value != 3 - or reduction2.params[1].value != 0 - or reduction2.params[2].value != 1 - ): - continue - - axes2 = checked_cast(list, layer.params[3].value) - if len(axes2) != 1: - continue - if axes2[0] != 2: - continue - - pooling = NcnnLayer( - "Pooling", - reduction2.name, - reduction2.num_inputs, - reduction2.num_outputs, - reduction2.inputs, - reduction2.outputs, - ) - pooling.add_param(0, 1) - pooling.add_param(4, 1) - - self.model.layers[j] = pooling - - pooling.inputs[0] = layer.inputs[0] - self.model.node_count -= 1 - self.model.blob_count -= 1 - layer.op_type = "ncnnfused" - - def __replace_prelu_with_leaky_relu(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "PReLU": - if layer.params[0].value != 1: - continue - - relu_layer = NcnnLayer( - "ReLU", - layer.name, - layer.num_inputs, - layer.num_outputs, - layer.inputs, - layer.outputs, - ) - relu_layer.add_param( - 0, checked_cast(float, layer.weight_data["slope"].weight[0]) - ) - - self.model.layers[i] = relu_layer - - def __replace_convolution_with_innerproduct_after_global_pooling(self): - for i, layer in enumerate(self.model.layers): - if layer.op_type == "Pooling": - if layer.params[4].value == 0: - continue - - # Pooling - Convolution - pooling_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Convolution": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == pooling_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - convolution = self.model.layers[j] - - innerproduct = NcnnLayer( - "InnerProduct", - convolution.name, - convolution.num_inputs, - convolution.num_outputs, - convolution.inputs, - convolution.outputs, - ) - innerproduct.add_param( - 0, checked_cast(int, convolution.params[0].value) - ) - innerproduct.add_param( - 1, checked_cast(int, convolution.params[5].value) - ) - innerproduct.add_param( - 2, checked_cast(int, convolution.params[6].value) - ) - innerproduct.add_param( - 8, checked_cast(int, convolution.params[8].value) - ) - innerproduct.add_param( - 9, checked_cast(int, convolution.params[9].value) - ) - innerproduct.add_param( - 10, - checked_cast(list, convolution.params[10].value), - ) - innerproduct.add_weight( - "weight", - convolution.weight_data["weight"].weight, - convolution.weight_data["weight"].quantize_tag, - ) - innerproduct.add_weight("bias", convolution.weight_data["bias"].weight) - - self.model.layers[j] = innerproduct - - def __replace_convolution_with_innerproduct_after_innerproduct(self): - while True: - replaced = False - for i, layer in enumerate(self.model.layers): - if layer.op_type == "InnerProduct": - # InnerProduct - Convolution - inprod_output = layer.outputs[0] - - j = i - for j in range(i + 1, len(self.model.layers)): - if self.model.layers[j].op_type != "Convolution": - continue - if self.model.layers[j].num_inputs != 1: - continue - if self.model.layers[j].inputs[0] == inprod_output: - break - else: - j += 1 - - if j == len(self.model.layers): - continue - - convolution = self.model.layers[j] - innerproduct2 = NcnnLayer( - "InnerProduct", - convolution.name, - convolution.num_inputs, - convolution.num_outputs, - convolution.inputs, - convolution.outputs, - ) - innerproduct2.add_param( - 0, checked_cast(int, convolution.params[0].value) - ) - innerproduct2.add_param( - 1, checked_cast(int, convolution.params[5].value) - ) - innerproduct2.add_param( - 2, checked_cast(int, convolution.params[6].value) - ) - innerproduct2.add_param( - 8, checked_cast(int, convolution.params[8].value) - ) - innerproduct2.add_param( - 9, checked_cast(int, convolution.params[9].value) - ) - innerproduct2.add_param( - 10, - checked_cast(list, convolution.params[10].value), - ) - innerproduct2.add_weight( - "weight", - convolution.weight_data["weight"].weight, - convolution.weight_data["weight"].quantize_tag, - ) - innerproduct2.add_weight( - "bias", convolution.weight_data["bias"].weight - ) - - self.model.layers[j] = innerproduct2 - - replaced = True - - if not replaced: - break - - def optimize(self) -> None: - self.__fuse_batchnorm_scale() - self.__fuse_x_batchnorm() - self.__fuse_x_mul() - self.__fuse_x_add() - self.__fuse_innerproduct_dropout() - - self.__replace_reduction_with_global_pooling() - self.__replace_prelu_with_leaky_relu() - - self.__fuse_x_activation() - self.__fuse_memorydata_binaryop() - self.__fuse_binaryop_eltwise() - - self.__eliminate_dropout() - self.__eliminate_pooling1x1() - self.__eliminate_noop() - self.__eliminate_split() - self.__eliminate_flatten_after_global_pooling() - self.__eliminate_reshape_after_global_pooling() - self.__eliminate_reshape_before_binaryop() - - self.__replace_convolution_with_innerproduct_after_global_pooling() - self.__replace_convolution_with_innerproduct_after_innerproduct() - - self.__eliminate_flatten_after_innerproduct() - self.__eliminate_orphaned_memorydata() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/param_schema.json b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/param_schema.json deleted file mode 100644 index f967075..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/param_schema.json +++ /dev/null @@ -1,2966 +0,0 @@ -{ - "AbsVal": { - "weightOrder": {} - }, - "ArgMax": { - "0": { - "paramId": "0", - "paramPhase": "out_max_val", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "topk", - "defaultValue": 1 - }, - "weightOrder": {} - }, - "BatchNorm": { - "0": { - "paramId": "0", - "paramPhase": "channels", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "eps", - "defaultValue": 0.0 - }, - "weightOrder": { - "slope": [1], - "mean": [1], - "variance": [1], - "bias": [1] - } - }, - "Bias": { - "0": { - "paramId": "0", - "paramPhase": "bias_data_size", - "defaultValue": 0 - }, - "weightOrder": { - "bias": [1] - } - }, - "BinaryOp": { - "0": { - "paramId": "0", - "paramPhase": "op_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "with_scalar", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "b", - "defaultValue": 0.0 - }, - "weightOrder": {} - }, - "BNLL": { - "weightOrder": {} - }, - "Cast": { - "0": { - "paramId": "0", - "paramPhase": "type_from", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "type_to", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Clip": { - "0": { - "paramId": "0", - "paramPhase": "min", - "defaultValue": "-FLT_MAX" - }, - "1": { - "paramId": "1", - "paramPhase": "max", - "defaultValue": "FLT_MAX" - }, - "weightOrder": {} - }, - "Concat": { - "0": { - "paramId": "0", - "paramPhase": "axis", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Convolution": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "int8_scale_term", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "18": { - "paramId": "18", - "paramPhase": "pad_value", - "defaultValue": 0.0 - }, - "19": { - "paramId": "19", - "paramPhase": "dynamic_weight", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Convolution1D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "18": { - "paramId": "18", - "paramPhase": "pad_value", - "defaultValue": 0.0 - }, - "19": { - "paramId": "19", - "paramPhase": "dynamic_weight", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Convolution3D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "17": { - "paramId": "17", - "paramPhase": "pad_behind", - "defaultValue": "pad_front" - }, - "18": { - "paramId": "18", - "paramPhase": "pad_value", - "defaultValue": 0.0 - }, - "21": { - "paramId": "21", - "paramPhase": "kernel_d", - "defaultValue": "kernel_w" - }, - "22": { - "paramId": "22", - "paramPhase": "dilation_d", - "defaultValue": "dilation_w" - }, - "23": { - "paramId": "23", - "paramPhase": "stride_d", - "defaultValue": "stride_w" - }, - "24": { - "paramId": "24", - "paramPhase": "pad_front", - "defaultValue": "pad_left" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "ConvolutionDepthWise": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "group", - "defaultValue": 1 - }, - "8": { - "paramId": "8", - "paramPhase": "int8_scale_term", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "18": { - "paramId": "18", - "paramPhase": "pad_value", - "defaultValue": 0.0 - }, - "19": { - "paramId": "19", - "paramPhase": "dynamic_weight", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "ConvolutionDepthWise1D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "group", - "defaultValue": 1 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "18": { - "paramId": "18", - "paramPhase": "pad_value", - "defaultValue": 0.0 - }, - "19": { - "paramId": "19", - "paramPhase": "dynamic_weight", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "ConvolutionDepthWise3D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "group", - "defaultValue": 1 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "17": { - "paramId": "17", - "paramPhase": "pad_behind", - "defaultValue": "pad_front" - }, - "18": { - "paramId": "18", - "paramPhase": "pad_value", - "defaultValue": 0.0 - }, - "21": { - "paramId": "21", - "paramPhase": "kernel_d", - "defaultValue": "kernel_w" - }, - "22": { - "paramId": "22", - "paramPhase": "dilation_d", - "defaultValue": "dilation_w" - }, - "23": { - "paramId": "23", - "paramPhase": "stride_d", - "defaultValue": "stride_w" - }, - "24": { - "paramId": "24", - "paramPhase": "pad_front", - "defaultValue": "pad_left" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Crop": { - "0": { - "paramId": "0", - "paramPhase": "woffset", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "hoffset", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "coffset", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "outw", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "outh", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "outc", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "woffset2", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "hoffset2", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "coffset2", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "starts", - "defaultValue": [] - }, - "10": { - "paramId": "10", - "paramPhase": "ends", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "axes", - "defaultValue": [] - }, - "weightOrder": {} - }, - "Deconvolution": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "18": { - "paramId": "18", - "paramPhase": "output_pad_right", - "defaultValue": 0 - }, - "19": { - "paramId": "19", - "paramPhase": "output_pad_bottom", - "defaultValue": "output_pad_right" - }, - "20": { - "paramId": "20", - "paramPhase": "output_w", - "defaultValue": 0 - }, - "21": { - "paramId": "21", - "paramPhase": "output_h", - "defaultValue": "output_w" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Deconvolution1D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "18": { - "paramId": "18", - "paramPhase": "output_pad_right", - "defaultValue": 0 - }, - "20": { - "paramId": "20", - "paramPhase": "output_w", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Deconvolution3D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "17": { - "paramId": "17", - "paramPhase": "pad_behind", - "defaultValue": "pad_front" - }, - "18": { - "paramId": "18", - "paramPhase": "output_pad_right", - "defaultValue": 0 - }, - "19": { - "paramId": "19", - "paramPhase": "output_pad_bottom", - "defaultValue": "output_pad_right" - }, - "20": { - "paramId": "20", - "paramPhase": "output_pad_behind", - "defaultValue": "output_pad_right" - }, - "21": { - "paramId": "21", - "paramPhase": "kernel_d", - "defaultValue": "kernel_w" - }, - "22": { - "paramId": "22", - "paramPhase": "dilation_d", - "defaultValue": "dilation_w" - }, - "23": { - "paramId": "23", - "paramPhase": "stride_d", - "defaultValue": "stride_w" - }, - "24": { - "paramId": "24", - "paramPhase": "pad_front", - "defaultValue": "pad_left" - }, - "25": { - "paramId": "25", - "paramPhase": "output_w", - "defaultValue": 0 - }, - "26": { - "paramId": "26", - "paramPhase": "output_h", - "defaultValue": "output_w" - }, - "27": { - "paramId": "27", - "paramPhase": "output_d", - "defaultValue": "output_w" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "DeconvolutionDepthWise": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "group", - "defaultValue": 1 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "18": { - "paramId": "18", - "paramPhase": "output_pad_right", - "defaultValue": 0 - }, - "19": { - "paramId": "19", - "paramPhase": "output_pad_bottom", - "defaultValue": "output_pad_right" - }, - "20": { - "paramId": "20", - "paramPhase": "output_w", - "defaultValue": 0 - }, - "21": { - "paramId": "21", - "paramPhase": "output_h", - "defaultValue": "output_w" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "DeconvolutionDepthWise1D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "group", - "defaultValue": 1 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "18": { - "paramId": "18", - "paramPhase": "output_pad_right", - "defaultValue": 0 - }, - "20": { - "paramId": "20", - "paramPhase": "output_w", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "DeconvolutionDepthWise3D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "group", - "defaultValue": 1 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "17": { - "paramId": "17", - "paramPhase": "pad_behind", - "defaultValue": "pad_front" - }, - "18": { - "paramId": "18", - "paramPhase": "output_pad_right", - "defaultValue": 0 - }, - "19": { - "paramId": "19", - "paramPhase": "output_pad_bottom", - "defaultValue": "output_pad_right" - }, - "20": { - "paramId": "20", - "paramPhase": "output_pad_behind", - "defaultValue": "output_pad_right" - }, - "21": { - "paramId": "21", - "paramPhase": "kernel_d", - "defaultValue": "kernel_w" - }, - "22": { - "paramId": "22", - "paramPhase": "dilation_d", - "defaultValue": "dilation_w" - }, - "23": { - "paramId": "23", - "paramPhase": "stride_d", - "defaultValue": "stride_w" - }, - "24": { - "paramId": "24", - "paramPhase": "pad_front", - "defaultValue": "pad_left" - }, - "25": { - "paramId": "25", - "paramPhase": "output_w", - "defaultValue": 0 - }, - "26": { - "paramId": "26", - "paramPhase": "output_h", - "defaultValue": "output_w" - }, - "27": { - "paramId": "27", - "paramPhase": "output_d", - "defaultValue": "output_w" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "DeformableConv2D": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "dilation_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "4": { - "paramId": "4", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "int8_scale_term", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "dilation_h", - "defaultValue": "dilation_w" - }, - "13": { - "paramId": "13", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Dequantize": { - "0": { - "paramId": "0", - "paramPhase": "scale", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "bias_data_size", - "defaultValue": 0 - }, - "weightOrder": { - "scale": [1], - "bias": [1] - } - }, - "DetectionOutput": { - "0": { - "paramId": "0", - "paramPhase": "num_class", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "nms_threshold", - "defaultValue": 0.05 - }, - "2": { - "paramId": "2", - "paramPhase": "nms_top_k", - "defaultValue": 300 - }, - "3": { - "paramId": "3", - "paramPhase": "keep_top_k", - "defaultValue": 100 - }, - "4": { - "paramId": "4", - "paramPhase": "confidence_threshold", - "defaultValue": 0.5 - }, - "5": { - "paramId": "5", - "paramPhase": "variances[0]", - "defaultValue": 0.1 - }, - "6": { - "paramId": "6", - "paramPhase": "variances[1]", - "defaultValue": 0.1 - }, - "7": { - "paramId": "7", - "paramPhase": "variances[2]", - "defaultValue": 0.2 - }, - "8": { - "paramId": "8", - "paramPhase": "variances[3]", - "defaultValue": 0.2 - }, - "weightOrder": {} - }, - "Dropout": { - "0": { - "paramId": "0", - "paramPhase": "scale", - "defaultValue": 1.0 - }, - "weightOrder": {} - }, - "Eltwise": { - "0": { - "paramId": "0", - "paramPhase": "op_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "coeffs", - "defaultValue": [] - }, - "weightOrder": {} - }, - "ELU": { - "0": { - "paramId": "0", - "paramPhase": "alpha", - "defaultValue": 0.1 - }, - "weightOrder": {} - }, - "Embed": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "input_dim", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "weightOrder": { - "weight": [1, 3, 10], - "bias": [1] - } - }, - "Exp": { - "0": { - "paramId": "0", - "paramPhase": "base", - "defaultValue": -1.0 - }, - "1": { - "paramId": "1", - "paramPhase": "scale", - "defaultValue": 1.0 - }, - "2": { - "paramId": "2", - "paramPhase": "shift", - "defaultValue": 0.0 - }, - "weightOrder": {} - }, - "ExpandDims": { - "0": { - "paramId": "0", - "paramPhase": "expand_w", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "expand_h", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "expand_c", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "axes", - "defaultValue": [] - }, - "weightOrder": {} - }, - "Flatten": { - "weightOrder": {} - }, - "GELU": { - "0": { - "paramId": "0", - "paramPhase": "fast_gelu", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Gemm": { - "0": { - "paramId": "0", - "paramPhase": "alpha", - "defaultValue": 1.0 - }, - "1": { - "paramId": "1", - "paramPhase": "beta", - "defaultValue": 1.0 - }, - "2": { - "paramId": "2", - "paramPhase": "transA", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "transB", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "GLU": { - "0": { - "paramId": "0", - "paramPhase": "axis", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "GridSample": { - "0": { - "paramId": "0", - "paramPhase": "sample_type", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "padding_mode", - "defaultValue": 1 - }, - "2": { - "paramId": "2", - "paramPhase": "align_corner", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "GroupNorm": { - "0": { - "paramId": "0", - "paramPhase": "group", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "channels", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "eps", - "defaultValue": 0.001 - }, - "3": { - "paramId": "3", - "paramPhase": "affine", - "defaultValue": 1 - }, - "weightOrder": { - "gamma": [1], - "beta": [1] - } - }, - "GRU": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "direction", - "defaultValue": 0 - }, - "weightOrder": { - "weight_xc": [1, 3, 10], - "bias": [1, 3, 10], - "weight_hc": [1, 3, 10] - } - }, - "HardSigmoid": { - "0": { - "paramId": "0", - "paramPhase": "alpha", - "defaultValue": 0.2 - }, - "1": { - "paramId": "1", - "paramPhase": "beta", - "defaultValue": 0.5 - }, - "weightOrder": {} - }, - "HardSwish": { - "0": { - "paramId": "0", - "paramPhase": "alpha", - "defaultValue": 0.2 - }, - "1": { - "paramId": "1", - "paramPhase": "beta", - "defaultValue": 0.5 - }, - "weightOrder": {} - }, - "InnerProduct": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "int8_scale_term", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "weightOrder": { - "weight": [1, 10], - "bias": [1] - } - }, - "Input": { - "0": { - "paramId": "0", - "paramPhase": "w", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "h", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "c", - "defaultValue": 0 - }, - "11": { - "paramId": "11", - "paramPhase": "d", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "InstanceNorm": { - "0": { - "paramId": "0", - "paramPhase": "channels", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "eps", - "defaultValue": 0.001 - }, - "2": { - "paramId": "2", - "paramPhase": "affine", - "defaultValue": 1 - }, - "weightOrder": { - "gamma": [1], - "bias": [1] - } - }, - "Interp": { - "0": { - "paramId": "0", - "paramPhase": "resize_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "height_scale", - "defaultValue": 1.0 - }, - "2": { - "paramId": "2", - "paramPhase": "width_scale", - "defaultValue": 1.0 - }, - "3": { - "paramId": "3", - "paramPhase": "output_height", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "output_width", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "dynamic_target_size", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "align_corner", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "LayerNorm": { - "0": { - "paramId": "0", - "paramPhase": "affine_size", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "eps", - "defaultValue": 0.001 - }, - "2": { - "paramId": "2", - "paramPhase": "affine", - "defaultValue": 1 - }, - "weightOrder": { - "gamma": [1], - "beta": [1] - } - }, - "Log": { - "0": { - "paramId": "0", - "paramPhase": "base", - "defaultValue": -1.0 - }, - "1": { - "paramId": "1", - "paramPhase": "scale", - "defaultValue": 1.0 - }, - "2": { - "paramId": "2", - "paramPhase": "shift", - "defaultValue": 0.0 - }, - "weightOrder": {} - }, - "LRN": { - "0": { - "paramId": "0", - "paramPhase": "region_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "local_size", - "defaultValue": 5 - }, - "2": { - "paramId": "2", - "paramPhase": "alpha", - "defaultValue": 1.0 - }, - "3": { - "paramId": "3", - "paramPhase": "beta", - "defaultValue": 0.75 - }, - "4": { - "paramId": "4", - "paramPhase": "bias", - "defaultValue": 1.0 - }, - "weightOrder": {} - }, - "LSTM": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "weight_data_size", - "defaultValue": 1 - }, - "2": { - "paramId": "2", - "paramPhase": "direction", - "defaultValue": 0 - }, - "weightOrder": { - "weight_xc": [1, 3, 10], - "bias_c": [1, 3, 10], - "weight_hc": [1, 3, 10], - "weight_hr": [1, 3, 10] - } - }, - "MemoryData": { - "0": { - "paramId": "0", - "paramPhase": "w", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "h", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "c", - "defaultValue": 0 - }, - "weightOrder": { - "data": [1] - } - }, - "Mish": { - "weightOrder": {} - }, - "MultiHeadAttention": { - "0": { - "paramId": "0", - "paramPhase": "embed_dim", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "num_head", - "defaultValue": 1 - }, - "2": { - "paramId": "2", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "weightOrder": { - "q_weight": [1, 3, 10], - "q_bias": [1], - "k_weight": [1, 3, 10], - "k_bias": [1], - "v_weight": [1, 3, 10], - "v_bias": [1], - "out_weight": [1, 3, 10], - "out_bias": [1] - } - }, - "MVN": { - "0": { - "paramId": "0", - "paramPhase": "normalize_variance", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "across_channels", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "eps", - "defaultValue": 0.0001 - }, - "weightOrder": {} - }, - "Noop": { - "weightOrder": {} - }, - "Normalize": { - "0": { - "paramId": "0", - "paramPhase": "across_spatial", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "channel_shared", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "eps", - "defaultValue": 0.0001 - }, - "3": { - "paramId": "3", - "paramPhase": "scale_data_size", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "across_channel", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "eps_mode", - "defaultValue": 0 - }, - "weightOrder": { - "scale": [1] - } - }, - "Packing": { - "0": { - "paramId": "0", - "paramPhase": "out_packing", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "use_padding", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "cast_type_from", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "cast_type_to", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "storage_type_from", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "storage_type_to", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Padding": { - "0": { - "paramId": "0", - "paramPhase": "top", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "bottom", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "left", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "right", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "type", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "value", - "defaultValue": 0.0 - }, - "6": { - "paramId": "6", - "paramPhase": "per_channel_pad_data_size", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "front", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "behind", - "defaultValue": 0 - }, - "weightOrder": { - "per_channel_pad_data": [1] - } - }, - "Permute": { - "0": { - "paramId": "0", - "paramPhase": "order_type", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "PixelShuffle": { - "0": { - "paramId": "0", - "paramPhase": "upscale_factor", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "mode", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Pooling": { - "0": { - "paramId": "0", - "paramPhase": "pooling_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "global_pooling", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "pad_mode", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "avgpool_count_include_pad", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "adaptive_pooling", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "out_w", - "defaultValue": 0 - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "13": { - "paramId": "13", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "18": { - "paramId": "18", - "paramPhase": "out_h", - "defaultValue": "out_w" - }, - "weightOrder": {} - }, - "Pooling1D": { - "0": { - "paramId": "0", - "paramPhase": "pooling_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "global_pooling", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "pad_mode", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "avgpool_count_include_pad", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "adaptive_pooling", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "out_w", - "defaultValue": 0 - }, - "14": { - "paramId": "14", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "weightOrder": {} - }, - "Pooling3D": { - "0": { - "paramId": "0", - "paramPhase": "pooling_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "kernel_w", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "stride_w", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "pad_left", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "global_pooling", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "pad_mode", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "avgpool_count_include_pad", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "adaptive_pooling", - "defaultValue": 0 - }, - "8": { - "paramId": "8", - "paramPhase": "out_w", - "defaultValue": 0 - }, - "11": { - "paramId": "11", - "paramPhase": "kernel_h", - "defaultValue": "kernel_w" - }, - "12": { - "paramId": "12", - "paramPhase": "stride_h", - "defaultValue": "stride_w" - }, - "13": { - "paramId": "13", - "paramPhase": "pad_top", - "defaultValue": "pad_left" - }, - "14": { - "paramId": "14", - "paramPhase": "pad_right", - "defaultValue": "pad_left" - }, - "15": { - "paramId": "15", - "paramPhase": "pad_bottom", - "defaultValue": "pad_top" - }, - "16": { - "paramId": "16", - "paramPhase": "pad_behind", - "defaultValue": "pad_front" - }, - "18": { - "paramId": "18", - "paramPhase": "out_h", - "defaultValue": "out_w" - }, - "21": { - "paramId": "21", - "paramPhase": "kernel_d", - "defaultValue": "kernel_w" - }, - "22": { - "paramId": "22", - "paramPhase": "stride_d", - "defaultValue": "stride_w" - }, - "23": { - "paramId": "23", - "paramPhase": "pad_front", - "defaultValue": "pad_left" - }, - "28": { - "paramId": "28", - "paramPhase": "out_d", - "defaultValue": "out_w" - }, - "weightOrder": {} - }, - "Power": { - "0": { - "paramId": "0", - "paramPhase": "power", - "defaultValue": 1.0 - }, - "1": { - "paramId": "1", - "paramPhase": "scale", - "defaultValue": 1.0 - }, - "2": { - "paramId": "2", - "paramPhase": "shift", - "defaultValue": 0.0 - }, - "weightOrder": {} - }, - "PReLU": { - "0": { - "paramId": "0", - "paramPhase": "num_slope", - "defaultValue": 0 - }, - "weightOrder": { - "slope": [1] - } - }, - "PriorBox": { - "0": { - "paramId": "0", - "paramPhase": "min_sizes", - "defaultValue": [] - }, - "1": { - "paramId": "1", - "paramPhase": "max_sizes", - "defaultValue": [] - }, - "2": { - "paramId": "2", - "paramPhase": "aspect_ratios", - "defaultValue": [] - }, - "3": { - "paramId": "3", - "paramPhase": "variances[0]", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "variances[1]", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "variances[2]", - "defaultValue": 0 - }, - "6": { - "paramId": "6", - "paramPhase": "variances[3]", - "defaultValue": 0 - }, - "7": { - "paramId": "7", - "paramPhase": "flip", - "defaultValue": 1 - }, - "8": { - "paramId": "8", - "paramPhase": "clip", - "defaultValue": 0 - }, - "9": { - "paramId": "9", - "paramPhase": "image_width", - "defaultValue": 0 - }, - "10": { - "paramId": "10", - "paramPhase": "image_height", - "defaultValue": 0 - }, - "11": { - "paramId": "11", - "paramPhase": "step_width", - "defaultValue": -233 - }, - "12": { - "paramId": "12", - "paramPhase": "step_height", - "defaultValue": -233 - }, - "13": { - "paramId": "13", - "paramPhase": "offset", - "defaultValue": 0 - }, - "14": { - "paramId": "14", - "paramPhase": "step_mmdetection", - "defaultValue": 0 - }, - "15": { - "paramId": "15", - "paramPhase": "center_mmdetection", - "defaultValue": 0 - }, - "weightOrder": { - "slope": [1] - } - }, - "Proposal": { - "0": { - "paramId": "0", - "paramPhase": "feat_stride", - "defaultValue": 16 - }, - "1": { - "paramId": "1", - "paramPhase": "base_size", - "defaultValue": 16 - }, - "2": { - "paramId": "2", - "paramPhase": "pre_nms_topN", - "defaultValue": 6000 - }, - "3": { - "paramId": "3", - "paramPhase": "after_nms_topN", - "defaultValue": 300 - }, - "4": { - "paramId": "4", - "paramPhase": "num_thresh", - "defaultValue": 0.7 - }, - "5": { - "paramId": "5", - "paramPhase": "min_size", - "defaultValue": 16 - }, - "weightOrder": {} - }, - "PSROIPooling": { - "0": { - "paramId": "0", - "paramPhase": "pooled_width", - "defaultValue": 7 - }, - "1": { - "paramId": "1", - "paramPhase": "pooled_height", - "defaultValue": 7 - }, - "2": { - "paramId": "2", - "paramPhase": "spatial_scale", - "defaultValue": 0.0625 - }, - "3": { - "paramId": "3", - "paramPhase": "output_dim", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Quantize": { - "0": { - "paramId": "0", - "paramPhase": "scale", - "defaultValue": 1 - }, - "weightOrder": { - "scale": [1] - } - }, - "Reduction": { - "0": { - "paramId": "0", - "paramPhase": "operation", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "reduce_all", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "coeff", - "defaultValue": 1.0 - }, - "3": { - "paramId": "3", - "paramPhase": "axes", - "defaultValue": [] - }, - "4": { - "paramId": "4", - "paramPhase": "keepdims", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "UNKNOWN", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "ReLU": { - "0": { - "paramId": "0", - "paramPhase": "slope", - "defaultValue": 0.0 - }, - "weightOrder": {} - }, - "Reorg": { - "0": { - "paramId": "0", - "paramPhase": "stride", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Requantize": { - "0": { - "paramId": "0", - "paramPhase": "scale_in_data_size", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "scale_out_data_size", - "defaultValue": 1 - }, - "2": { - "paramId": "2", - "paramPhase": "bias_data_size", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "activation_type", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "activation_params", - "defaultValue": [] - }, - "weightOrder": { - "scale_in": [1], - "scale_out": [1], - "bias": [1] - } - }, - "Reshape": { - "0": { - "paramId": "0", - "paramPhase": "w", - "defaultValue": -233 - }, - "1": { - "paramId": "1", - "paramPhase": "h", - "defaultValue": -233 - }, - "2": { - "paramId": "2", - "paramPhase": "c", - "defaultValue": -233 - }, - "3": { - "paramId": "3", - "paramPhase": "permute", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "RNN": { - "0": { - "paramId": "0", - "paramPhase": "num_output", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "weight_data_size", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "direction", - "defaultValue": 0 - }, - "weightOrder": { - "weight_xc": [1, 3, 10], - "bias_c": [1, 3, 10], - "weight_hc": [1, 3, 10] - } - }, - "ROIAlign": { - "0": { - "paramId": "0", - "paramPhase": "pooled_width", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "pooled_height", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "spatial_scale", - "defaultValue": 1 - }, - "3": { - "paramId": "3", - "paramPhase": "sampling_ratio", - "defaultValue": 0 - }, - "4": { - "paramId": "4", - "paramPhase": "aligned", - "defaultValue": 0 - }, - "5": { - "paramId": "5", - "paramPhase": "version", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "ROIPooling": { - "0": { - "paramId": "0", - "paramPhase": "pooled_width", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "pooled_height", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "spatial_scale", - "defaultValue": 1 - }, - "weightOrder": {} - }, - "Scale": { - "0": { - "paramId": "0", - "paramPhase": "scale_data_size", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "bias_term", - "defaultValue": 0 - }, - "weightOrder": { - "scale": [1], - "bias": [1] - } - }, - "SELU": { - "0": { - "paramId": "0", - "paramPhase": "alpha", - "defaultValue": 1.67326324 - }, - "1": { - "paramId": "1", - "paramPhase": "lambda", - "defaultValue": 1.050700987 - }, - "weightOrder": {} - }, - "ShuffleChannel": { - "0": { - "paramId": "0", - "paramPhase": "group", - "defaultValue": 1 - }, - "1": { - "paramId": "1", - "paramPhase": "reverse", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Sigmoid": { - "weightOrder": {} - }, - "Slice": { - "0": { - "paramId": "0", - "paramPhase": "slices", - "defaultValue": [] - }, - "1": { - "paramId": "1", - "paramPhase": "axis", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Softmax": { - "0": { - "paramId": "0", - "paramPhase": "axis", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "fixbug0", - "defaultValue": 1 - }, - "weightOrder": {} - }, - "Softplus": { - "weightOrder": {} - }, - "Split": { - "weightOrder": {} - }, - "SPP": { - "0": { - "paramId": "0", - "paramPhase": "pooling_type", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "pyramid_height", - "defaultValue": 1 - }, - "weightOrder": {} - }, - "Squeeze": { - "0": { - "paramId": "0", - "paramPhase": "squeeze_w", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "squeeze_h", - "defaultValue": 0 - }, - "2": { - "paramId": "2", - "paramPhase": "squeeze_c", - "defaultValue": 0 - }, - "3": { - "paramId": "3", - "paramPhase": "axes", - "defaultValue": [] - }, - "weightOrder": {} - }, - "StatisticsPooling": { - "0": { - "paramId": "0", - "paramPhase": "include_stddev", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "Swish": { - "weightOrder": {} - }, - "TanH": { - "weightOrder": {} - }, - "Threshold": { - "0": { - "paramId": "0", - "paramPhase": "threshold", - "defaultValue": 0.0 - }, - "weightOrder": {} - }, - "Tile": { - "0": { - "paramId": "0", - "paramPhase": "dim", - "defaultValue": 0 - }, - "1": { - "paramId": "1", - "paramPhase": "tiles", - "defaultValue": 1 - }, - "2": { - "paramId": "2", - "paramPhase": "repeats", - "defaultValue": [] - }, - "weightOrder": {} - }, - "UnaryOp": { - "0": { - "paramId": "0", - "paramPhase": "op_type", - "defaultValue": 0 - }, - "weightOrder": {} - }, - "YoloDetectionOutput": { - "0": { - "paramId": "0", - "paramPhase": "num_class", - "defaultValue": 20 - }, - "1": { - "paramId": "1", - "paramPhase": "num_box", - "defaultValue": 5 - }, - "2": { - "paramId": "2", - "paramPhase": "confidence_threshold", - "defaultValue": 0.01 - }, - "3": { - "paramId": "3", - "paramPhase": "num_threshold", - "defaultValue": 0.45 - }, - "4": { - "paramId": "4", - "paramPhase": "biases", - "defaultValue": [] - }, - "weightOrder": {} - }, - "Yolov3DetectionOutput": { - "0": { - "paramId": "0", - "paramPhase": "num_class", - "defaultValue": 20 - }, - "1": { - "paramId": "1", - "paramPhase": "num_box", - "defaultValue": 5 - }, - "2": { - "paramId": "2", - "paramPhase": "confidence_threshold", - "defaultValue": 0.01 - }, - "3": { - "paramId": "3", - "paramPhase": "num_threshold", - "defaultValue": 0.45 - }, - "4": { - "paramId": "4", - "paramPhase": "biases", - "defaultValue": [] - }, - "5": { - "paramId": "5", - "paramPhase": "mask", - "defaultValue": [] - }, - "6": { - "paramId": "6", - "paramPhase": "anchors_scale", - "defaultValue": [] - }, - "weightOrder": {} - } -} diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/session.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/session.py deleted file mode 100644 index 7abee6a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/ncnn/session.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -import tempfile -from weakref import WeakKeyDictionary - -try: - from ncnn_vulkan import ncnn - - use_gpu = True -except ImportError: - from ncnn import ncnn # type: ignore - - use_gpu = False - -from packages.chaiNNer_ncnn.settings import NcnnSettings - -from .model import NcnnModelWrapper - - -def create_ncnn_net(model: NcnnModelWrapper, settings: NcnnSettings) -> ncnn.Net: - net = ncnn.Net() - - if model.fp == "fp16": - net.opt.use_fp16_packed = True - net.opt.use_fp16_storage = True - net.opt.use_fp16_arithmetic = True - else: - net.opt.use_fp16_packed = False - net.opt.use_fp16_storage = False - net.opt.use_fp16_arithmetic = False - - if use_gpu: - # Use vulkan compute - net.opt.use_vulkan_compute = True - net.set_vulkan_device(settings.gpu_index) - else: - # Configure Winograd/SGEMM optimizations - net.opt.use_winograd_convolution = settings.winograd - net.opt.use_sgemm_convolution = settings.sgemm - # Configure multithreading - net.opt.num_threads = settings.threads - net.opt.openmp_blocktime = settings.blocktime - - # Load model param and bin - net.load_param_mem(model.model.write_param()) - if use_gpu: - net.load_model_mem(model.model.bin) - else: - with tempfile.TemporaryDirectory() as tmp_model_dir: - bin_filename = tmp_model_dir + "/ncnn-model.bin" - model.model.write_bin(bin_filename) - net.load_model(bin_filename) - - return net - - -__session_cache: WeakKeyDictionary[NcnnModelWrapper, ncnn.Net] = WeakKeyDictionary() - - -def get_ncnn_net(model: NcnnModelWrapper, settings: NcnnSettings) -> ncnn.Net: - cached = __session_cache.get(model) - if cached is None: - cached = create_ncnn_net(model, settings=settings) - __session_cache[model] = cached - return cached diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise.py deleted file mode 100644 index bcc45b2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise.py +++ /dev/null @@ -1,148 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from typing import Callable - -import numpy as np - -from ..utils.utils import get_h_w_c -from .image_utils import as_target_channels - - -def __add_noises( - image: np.ndarray, - noise_gen: Callable[[int, int], list[np.ndarray]], - combine: Callable[[np.ndarray, list[np.ndarray]], np.ndarray], -) -> np.ndarray: - img = image - h, w, c = get_h_w_c(img) - assert c != 2, "Noise cannot be added to 2-channel images." - - if c > 3: - img = img[:, :, :3] - - noises = noise_gen(h, w) - - assert len(noises) > 0 - - max_channels = min(c, 3) - for n in noises: - noise_channels = get_h_w_c(n)[2] - assert noise_channels in (1, 3), "Noise must be a grayscale or RGB image." - max_channels = max(max_channels, noise_channels) - - noises = [as_target_channels(n, max_channels) for n in noises] - img = as_target_channels(img, max_channels) - - result = combine(img, noises) - - if c > 3: - result = np.concatenate([result, image[:, :, 3:]], axis=2) - - return np.clip(result, 0, 1) - - -def __add_noise( - image: np.ndarray, - noise_gen: Callable[[int, int], np.ndarray], - combine: Callable[[np.ndarray, np.ndarray], np.ndarray] = lambda i, n: i + n, -) -> np.ndarray: - return __add_noises( - image, - lambda h, w: [noise_gen(h, w)], - lambda i, n: combine(i, n[0]), - ) - - -class NoiseColor(Enum): - RGB = "rgb" - GRAY = "gray" - - @property - def channels(self): - return 3 if self is NoiseColor.RGB else 1 - - -# Applies gaussian noise to an image -def gaussian_noise( - image: np.ndarray, - amount: float, - noise_color: NoiseColor, - seed: int = 0, -) -> np.ndarray: - rng = np.random.default_rng(seed) - return __add_noise( - image, - lambda h, w: rng.normal(0, amount, (h, w, noise_color.channels)).astype( - np.float32 - ), - ) - - -# Applies uniform noise to an image -def uniform_noise( - image: np.ndarray, - amount: float, - noise_color: NoiseColor, - seed: int = 0, -) -> np.ndarray: - rng = np.random.default_rng(seed) - return __add_noise( - image, - lambda h, w: rng.uniform(-amount, amount, (h, w, noise_color.channels)).astype( - np.float32 - ), - ) - - -# Applies salt and pepper noise to an image -def salt_and_pepper_noise( - image: np.ndarray, - amount: float, - noise_color: NoiseColor, - seed: int = 0, -) -> np.ndarray: - def gen_noise(h: int, w: int): - rng = np.random.default_rng(seed) - noise_c = noise_color.channels - amt = amount / 2 - pepper = rng.choice([0, 1], (h, w, noise_c), p=[amt, 1 - amt]).astype(np.uint8) - salt = rng.choice([0, 1], (h, w, noise_c), p=[1 - amt, amt]).astype(np.uint8) - return [pepper, salt] - - def combine(i: np.ndarray, n: list[np.ndarray]): - pepper, salt = n - return np.where(salt == 1, 1, np.where(pepper == 0, 0, i)) - - return __add_noises(image, gen_noise, combine) - - -# Applies poisson noise to an image -def poisson_noise( - image: np.ndarray, - amount: float, - noise_color: NoiseColor, - seed: int = 0, -) -> np.ndarray: - rng = np.random.default_rng(seed) - return __add_noise( - image, - lambda h, w: rng.poisson(amount, (h, w, noise_color.channels)).astype(np.uint8), - ) - - -# Applies speckle noise to an image -def speckle_noise( - image: np.ndarray, - amount: float, - noise_color: NoiseColor, - seed: int = 0, -) -> np.ndarray: - rng = np.random.default_rng(seed) - return __add_noise( - image, - lambda h, w: rng.normal(0, amount, (h, w, noise_color.channels)).astype( - np.float32 - ), - lambda i, n: i + i * n, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/blue.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/blue.py deleted file mode 100644 index 9bb82cd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/blue.py +++ /dev/null @@ -1,119 +0,0 @@ -# http://momentsingraphics.de/BlueNoise.html -# https://github.com/MomentsInGraphics/BlueNoise/blob/master/BlueNoise.py - -from __future__ import annotations - -import numpy as np -from scipy import ndimage - - -def find_largest_void(binary_pattern: np.ndarray, standard_deviation: float): - """This function returns the indices of the largest void in the given binary - pattern as defined by Ulichney. - @param BinaryPattern A boolean array (should be two-dimensional although the - implementation works in arbitrary dimensions). - @param StandardDeviation The standard deviation used for the Gaussian filter - in pixels. This can be a single float for an isotropic Gaussian or a - tuple with one float per dimension for an anisotropic Gaussian. - @return A flat index i such that BinaryPattern.flat[i] corresponds to the - largest void. By definition this is a majority pixel. - @sa GetVoidAndClusterBlueNoise""" - # The minority value is always True for convenience - if np.count_nonzero(binary_pattern) * 2 >= np.size(binary_pattern): - binary_pattern = np.logical_not(binary_pattern) - # Apply the Gaussian. We do not want to cut off the Gaussian at all because even - # the tiniest difference can change the ranking. Therefore we apply the Gaussian - # through a fast Fourier transform by means of the convolution theorem. - filtered_array = np.fft.ifftn( - ndimage.fourier.fourier_gaussian( - np.fft.fftn(np.where(binary_pattern, 1.0, 0.0)), standard_deviation - ) - ).real - # Find the largest void - return np.argmin(np.where(binary_pattern, 2.0, filtered_array)) - - -def find_tightest_cluster(binary_pattern: np.ndarray, standard_deviation: float): - """Like FindLargestVoid() but finds the tightest cluster which is a minority - pixel by definition. - @sa GetVoidAndClusterBlueNoise""" - if np.count_nonzero(binary_pattern) * 2 >= np.size(binary_pattern): - binary_pattern = np.logical_not(binary_pattern) - filtered_array = np.fft.ifftn( - ndimage.fourier.fourier_gaussian( - np.fft.fftn(np.where(binary_pattern, 1.0, 0.0)), standard_deviation - ) - ).real - return np.argmax(np.where(binary_pattern, filtered_array, -1.0)) - - -def create_blue_noise( - output_shape: tuple[int, int], - standard_deviation: float = 1.5, - initial_seed_fraction: float = 0.1, - seed: int = 0, -): - """Generates a blue noise dither array of the given shape using the method - proposed by Ulichney [1993] in "The void-and-cluster method for dither array - generation" published in Proc. SPIE 1913. - @param OutputShape The shape of the output array. This function works in - arbitrary dimension, i.e. OutputShape can have arbitrary length. Though - it is only tested for the 2D case where you should pass a tuple - (Height,Width). - @param StandardDeviation The standard deviation in pixels used for the - Gaussian filter defining largest voids and tightest clusters. Larger - values lead to more low-frequency content but better isotropy. Small - values lead to more ordered patterns with less low-frequency content. - Ulichney proposes to use a value of 1.5. If you want an anisotropic - Gaussian, you can pass a tuple of length len(OutputShape) with one - standard deviation per dimension. - @param initial_seed_fraction The only non-deterministic step in the algorithm - marks a small number of pixels in the grid randomly. This parameter - defines the fraction of such points. It has to be positive but less - than 0.5. Very small values lead to ordered patterns, beyond that there - is little change. - @return An integer array of shape OutputShape containing each integer from 0 - to np.prod(OutputShape)-1 exactly once.""" - n_rank = np.prod(output_shape) - # Generate the initial binary pattern with a prescribed number of ones - n_initial_one = max( - 1, min(int((n_rank - 1) / 2), int(n_rank * initial_seed_fraction)) - ) - # Start from white noise (this is the only randomized step) - initial_binary_pattern = np.zeros(output_shape, dtype=np.bool_) - initial_binary_pattern.flat = ( - np.random.default_rng(seed).permutation(np.arange(n_rank)) < n_initial_one - ) # type:ignore - # Swap ones from tightest clusters to largest voids iteratively until convergence - while True: - i_tightest_cluster = find_tightest_cluster( - initial_binary_pattern, standard_deviation - ) - initial_binary_pattern.flat[i_tightest_cluster] = False - i_largest_void = find_largest_void(initial_binary_pattern, standard_deviation) - if i_largest_void == i_tightest_cluster: - initial_binary_pattern.flat[i_tightest_cluster] = True - # Nothing has changed, so we have converged - break - else: - initial_binary_pattern.flat[i_largest_void] = True - # Rank all pixels - dither_array = np.zeros(output_shape, dtype=np.int32) - # Phase 1: Rank minority pixels in the initial binary pattern - binary_pattern = np.copy(initial_binary_pattern) - for rank in range(n_initial_one - 1, -1, -1): - i_tightest_cluster = find_tightest_cluster(binary_pattern, standard_deviation) - binary_pattern.flat[i_tightest_cluster] = False - dither_array.flat[i_tightest_cluster] = rank - # Phase 2: Rank the remainder of the first half of all pixels - binary_pattern = initial_binary_pattern - for rank in range(n_initial_one, int((n_rank + 1) / 2)): - i_largest_void = find_largest_void(binary_pattern, standard_deviation) - binary_pattern.flat[i_largest_void] = True - dither_array.flat[i_largest_void] = rank - # Phase 3: Rank the last half of pixels - for rank in range(int((n_rank + 1) / 2), n_rank): - i_tightest_cluster = find_tightest_cluster(binary_pattern, standard_deviation) - binary_pattern.flat[i_tightest_cluster] = True - dither_array.flat[i_tightest_cluster] = rank - return dither_array diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/noise_generator.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/noise_generator.py deleted file mode 100644 index 931064b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/noise_generator.py +++ /dev/null @@ -1,8 +0,0 @@ -from abc import ABC, abstractmethod - -import numpy as np - - -class NoiseGenerator(ABC): - @abstractmethod - def evaluate(self, points: np.ndarray) -> np.ndarray: ... diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/simplex.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/simplex.py deleted file mode 100644 index 7d04cd1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/simplex.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Simplex noise implementation by Alex Dodge, 2023 - -References: - -Simplex noise demystified, Stefan Gustavson (2005) -http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf -""" - -from __future__ import annotations - -import itertools - -import numpy as np -from typing_extensions import override - -from .noise_generator import NoiseGenerator - -# fmt: off -PERMUTATION_TABLE_ARRAY = np.array([ - 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, - 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, - 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, - 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, - 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, - 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, - 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, - 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, - 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, - 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, - 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, - 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, - 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, - 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 -], dtype=np.int32) -# fmt: on - -# Empirically determined scaling factor for different numbers of dimensions -SCALE = { - 2: 50, - 3: 39, - 4: 32, - 5: 28, - 6: 26, - # It looks terrible at this many dimensions anyway -} - - -class SimplexNoise(NoiseGenerator): - def __init__(self, dimensions: int, seed: int | None, r2: float = 0.5): - if dimensions <= 0: - raise ValueError - if dimensions == 1: - raise RuntimeError("1D Simplex noise is not implemented here.") - if dimensions > 6: - raise RuntimeError("7D+ Simplex noise is not implemented here.") - - self.dimensions = dimensions - self.r2 = r2 - self.F = (np.sqrt(self.dimensions + 1) - 1) / self.dimensions - self.G = (1 - 1 / np.sqrt(self.dimensions + 1)) / self.dimensions - - """ - For 2D noise, we pick 16 gradients evenly distributed around the unit circle. - For 3D and above, we pick gradients pointing at the midpoints of the edges of a hypercube centered on the origin - """ - - if self.dimensions == 2: - n_gradients = 16 - self.gradients = np.array( - [ - ( - np.cos(2 * np.pi * i / n_gradients), - np.sin(2 * np.pi * i / n_gradients), - ) - for i in range(n_gradients) - ] - ) - else: - n_gradients = self.dimensions * 2 ** (self.dimensions - 1) - self.gradients = np.zeros((n_gradients, self.dimensions)) - for zero_dim in range(self.dimensions): - for i, vec in enumerate( - itertools.product([-1, 1], repeat=self.dimensions - 1) - ): - idx = zero_dim * 2 ** (self.dimensions - 1) + i - self.gradients[idx, :zero_dim] = vec[:zero_dim] - self.gradients[idx, zero_dim + 1 :] = vec[zero_dim:] - - if seed is None: - # Use the canonical table from the reference implementation - self.permutation_table = PERMUTATION_TABLE_ARRAY - else: - self.permutation_table = np.arange(self.gradients.shape[0] * 16) - np.random.default_rng(seed).shuffle(self.permutation_table) - - @override - def evaluate(self, points: np.ndarray): - n_points = points.shape[0] - assert points.shape == (n_points, self.dimensions) - - skewed_points = points + (points.sum(axis=1) * self.F).reshape((n_points, 1)) - skewed_bases, skewed_points_remainder = np.divmod(skewed_points, 1) - - skewed_simplex_verts = np.full( - (n_points, self.dimensions + 1, self.dimensions), - fill_value=skewed_bases.reshape((n_points, 1, -1)), - dtype="int32", - ) - - skewed_simplex_verts[:, self.dimensions, :] += 1 - for i in range(1, self.dimensions): - largest_dimension = np.argmax(skewed_points_remainder, axis=1) - for o in range(self.dimensions): - skewed_simplex_verts[ - (largest_dimension == o), i : self.dimensions, o - ] += 1 - if i != self.dimensions - 1: - skewed_points_remainder[(largest_dimension == o), o] = -1 - - gradient_index = np.zeros(skewed_simplex_verts.shape[:2], dtype="int32") - for i in range(skewed_simplex_verts.shape[2]): - gradient_index = ( - gradient_index + skewed_simplex_verts[:, :, i] - ) % self.permutation_table.size - gradient_index = self.permutation_table[gradient_index] - gradients = self.gradients[gradient_index % self.gradients.shape[0]] - - simplex_verts = ( - skewed_simplex_verts - - skewed_simplex_verts.sum(axis=2).reshape((n_points, -1, 1)) * self.G - ) - displacement = np.power( - points.reshape((n_points, 1, -1)) - simplex_verts, 2 - ).sum(axis=2) - dot_gradient = np.sum( - (points.reshape((n_points, 1, -1)) - simplex_verts) * gradients, axis=2 - ) - contributions = ( - np.power(np.maximum(0, self.r2 - displacement), 4) * dot_gradient - ) - - return np.sum(contributions, axis=1) * SCALE[self.dimensions] + 0.5 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/value.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/value.py deleted file mode 100644 index be7d9b0..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/noise_functions/value.py +++ /dev/null @@ -1,50 +0,0 @@ -import itertools - -import numpy as np -from typing_extensions import override - -from .noise_generator import NoiseGenerator - - -def smoothstep(t: np.ndarray): - return t * t * (3 - 2 * t) - - -class ValueNoise(NoiseGenerator): - def __init__(self, dimensions: int, seed: int, smooth: bool): - self.dimensions = dimensions - self.smooth = smooth - - self.values: np.ndarray = np.arange(16, dtype="float32") - self.values = self.values / max(self.values) - - self.permutation_table = np.arange(self.values.size * 16) - np.random.default_rng(seed).shuffle(self.permutation_table) - - @override - def evaluate(self, points: np.ndarray): - block, fractional = np.divmod(points, 1) - if self.smooth: - fractional = smoothstep(fractional) - - corners = np.zeros( - (points.shape[0], 2**self.dimensions, self.dimensions), dtype="int32" - ) - weights = np.zeros((points.shape[0], 2**self.dimensions)) - - for i, pattern in enumerate(itertools.product([0, 1], repeat=self.dimensions)): - np_pattern = np.array(pattern, dtype=np.int32) - corners[:, i] = block + np_pattern - - # linear interpolation - weights[:, i] = np.prod( - fractional * np_pattern + (1 - fractional) * (1 - np_pattern), axis=1 - ) - - value_index = np.zeros(corners.shape[:2], dtype="int32") - for i in range(corners.shape[2]): - value_index = (value_index + corners[:, :, i]) % self.permutation_table.size - value_index = self.permutation_table[value_index] - values = self.values[value_index % self.values.size] - - return np.sum(values * weights, axis=1) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/addition.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/addition.py deleted file mode 100644 index f906887..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/addition.py +++ /dev/null @@ -1,133 +0,0 @@ -import math -from enum import Enum - -import numpy as np - -from .util import XYZ, normalize_normals - - -class AdditionMethod(Enum): - PARTIAL_DERIVATIVES = 0 - """ - The addition works by converting the normals into 2D slopes and then adding - the slopes. The sum of the slopes is then converted back into normals. - - When adding 2 normal maps, the normals themselves are not added; - Instead, the heightmaps that those normals represent are added. - Conceptually, this entails converting the normals into slopes - (the derivatives of the heightmap), integrating the slopes to get - the heightmaps, adding the heightmaps, then performing the reverse - on the added heightmaps. Practically, this is unnecessary, as adding - the slopes together is equivalent to adding the heightmaps. - """ - ANGLES = 1 - """ - The addition works by converting the normals into 2 angles, one angle the - X axis and one along the Y axis. Those 2 angles are then added together. - - Since this might create angles outside the range of -90° to 90°, - the resulting angles are clamped to this range. - """ - - -def __partial_derivatives(n1: XYZ, n2: XYZ, f1: float, f2: float) -> XYZ: - x1, y1, z1 = n1 - x2, y2, z2 = n2 - - # Slopes aren't defined for z=0, so set to near-zero decimal - z1 = np.maximum(z1, 0.001, out=z1) - z2 = np.maximum(z2, 0.001, out=z2) - - # This works as follows: - # 1. Use the normals n,m to calculate 3D planes (the slopes) centered at origin p_n,p_m. - # 2. Calculate the Z values of those planes at a_xy=(1,0) and b_xy=(0,1). - # 3. Add the Z values to together (weighted using their strength): - # a_z = p_n[a_xy] * n_strength + p_m[a_xy] * m_strength, same for b_xy. - # 4. Define a=(1,0,a_z), b=(0,1,b_z). - # 5. The final normal will be normalize(cross(a,b)). - # This works out as: - - n_f = f1 / z1 - m_f = f2 / z2 - - x = x1 * n_f + x2 * m_f - y = y1 * n_f + y2 * m_f - - l_r = 1 / np.sqrt(np.square(x) + np.square(y) + 1) - x *= l_r - y *= l_r - z = l_r - - return x, y, z - - -def __clamp_angles(angles: np.ndarray) -> np.ndarray: - return np.clip(angles, -math.pi / 2, math.pi / 2) - - -def __angles(n1: XYZ, n2: XYZ, f1: float, f2: float) -> XYZ: - x1, y1, _ = n1 - x2, y2, _ = n2 - - return normalize_normals( - np.sin(__clamp_angles(np.arcsin(x1) * f1 + np.arcsin(x2) * f2)), - np.sin(__clamp_angles(np.arcsin(y1) * f1 + np.arcsin(y2) * f2)), - ) - - -def add_normals( - method: AdditionMethod, - n1: np.ndarray, - n2: np.ndarray, - f1: float = 1, - f2: float = 1, -) -> XYZ: - # Convert BGR to XY - x1 = n1[:, :, 2] * 2 - 1 - y1 = n1[:, :, 1] * 2 - 1 - x2 = n2[:, :, 2] * 2 - 1 - y2 = n2[:, :, 1] * 2 - 1 - - xyz1 = normalize_normals(x1, y1) - xyz2 = normalize_normals(x2, y2) - - if method is AdditionMethod.PARTIAL_DERIVATIVES: - return __partial_derivatives(xyz1, xyz2, f1, f2) - elif method is AdditionMethod.ANGLES: - return __angles(xyz1, xyz2, f1, f2) - else: - raise AssertionError(f"Invalid normal addition method {method}") - - -def strengthen_normals(method: AdditionMethod, n: np.ndarray, f: float) -> XYZ: - """ - Same as `add_normals`, but with `n2` being the same as `n1` and `f2=0`. - """ - # Convert BGR to XY - x = n[:, :, 2] * 2 - 1 - y = n[:, :, 1] * 2 - 1 - - x, y, z = normalize_normals(x, y) - - if method is AdditionMethod.PARTIAL_DERIVATIVES: - # Slopes aren't defined for z=0, so set to near-zero decimal - z = np.maximum(z, 0.001, out=z) - - n_f = f / z - - x = x * n_f - y = y * n_f - - l_r = 1 / np.sqrt(np.square(x) + np.square(y) + 1) - x *= l_r - y *= l_r - z = l_r - - return x, y, z - elif method is AdditionMethod.ANGLES: - return normalize_normals( - np.sin(__clamp_angles(np.arcsin(x) * f)), - np.sin(__clamp_angles(np.arcsin(y) * f)), - ) - else: - raise AssertionError(f"Invalid normal addition method {method}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/edge_filter.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/edge_filter.py deleted file mode 100644 index 098e120..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/edge_filter.py +++ /dev/null @@ -1,172 +0,0 @@ -from __future__ import annotations - -import math -from enum import Enum - -import numpy as np - -a = [ - [1 / 16, 1 / 10, 0, -1 / 10, -1 / 16], - [1 / 10, 1 / 2.8, 0, -1 / 2.8, -1 / 10], - [1 / 8, 1 / 2.0, 0, -1 / 2.0, -1 / 8], - [1 / 10, 1 / 2.8, 0, -1 / 2.8, -1 / 10], - [1 / 16, 1 / 10, 0, -1 / 10, -1 / 16], -] -a = [ - [1, 1.6, 0, -1.6, -1 / 16], - [1.6, 16 / 2.8, 0, -1 / 2.8, -1 / 10], - [2, 8, 0, -1 / 2.0, -1 / 8], - [1.6, 16 / 2.8, 0, -1 / 2.8, -1 / 10], - [1, 1.6, 0, -1.6, -1 / 16], -] - - -class EdgeFilter(Enum): - SOBEL = "sobel" - SOBEL_LIKE_5 = "sobel-like-5" - SOBEL_LIKE_7 = "sobel-like-7" - SOBEL_LIKE_9 = "sobel-like-9" - PREWITT = "prewitt" - SCHARR = "scharr" - FOUR_SAMPLE = "4-sample" - - MULTI_GAUSS = "multi-gauss" - - -FILTERS_X: dict[EdgeFilter, np.ndarray] = { - EdgeFilter.SOBEL: np.array( - [ - [+1, 0, -1], - [+2, 0, -2], - [+1, 0, -1], - ] - ), - EdgeFilter.SOBEL_LIKE_5: np.array( - [ - [1 / 16, 1 / 10, 0, -1 / 10, -1 / 16], - [1 / 10, 1 / 2.8, 0, -1 / 2.8, -1 / 10], - [1 / 8, 1 / 2.0, 0, -1 / 2.0, -1 / 8], - [1 / 10, 1 / 2.8, 0, -1 / 2.8, -1 / 10], - [1 / 16, 1 / 10, 0, -1 / 10, -1 / 16], - ] - ), - EdgeFilter.SOBEL_LIKE_7: np.array( - [ - [1, 2, 3, 0, -3, -2, -1], - [2, 3, 4, 0, -4, -3, -2], - [3, 4, 5, 0, -5, -4, -3], - [4, 5, 6, 0, -6, -5, -4], - [3, 4, 5, 0, -5, -4, -3], - [2, 3, 4, 0, -4, -3, -2], - [1, 2, 3, 0, -3, -2, -1], - ] - ), - EdgeFilter.SOBEL_LIKE_9: np.array( - [ - [1, 2, 3, 4, 0, -4, -3, -2, -1], - [2, 3, 4, 5, 0, -5, -4, -3, -2], - [3, 4, 5, 6, 0, -6, -5, -4, -3], - [4, 5, 6, 7, 0, -7, -6, -5, -4], - [5, 6, 7, 8, 0, -8, -7, -6, -5], - [4, 5, 6, 7, 0, -7, -6, -5, -4], - [3, 4, 5, 6, 0, -6, -5, -4, -3], - [2, 3, 4, 5, 0, -5, -4, -3, -2], - [1, 2, 3, 4, 0, -4, -3, -2, -1], - ] - ), - EdgeFilter.PREWITT: np.array( - [ - [+1, 0, -1], - [+1, 0, -1], - [+1, 0, -1], - ] - ), - EdgeFilter.SCHARR: np.array( - [ - [+3, 0, -3], - [+10, 0, -10], - [+3, 0, -3], - ] - ), - EdgeFilter.FOUR_SAMPLE: np.array( - [ - [1, 0, -1], - ] - ), -} - - -def create_gauss_kernel(parameters: list[tuple[float, float]]) -> np.ndarray: - """ - Parameters is a list of tuples (sigma, weight). - """ - - # We will use 2D gauss functions normalized to a volume of 1. Wikipedia - # has a nice article about this, so look it up if you want to know more: - # https://en.wikipedia.org/wiki/Gaussian_function#Two-dimensional_Gaussian_function - # - # We will use one scaled gauss function for each parameter scaled by its weight. - # All gauss function will then be added together. This means that the total - # volume will be the sum of all weights. - - total_volume = sum(weight for _, weight in parameters) - if total_volume == 0: - # this case doesn't really make sense, so GIGO - return np.zeros((1, 1)) - - def sample(x: float, y: float) -> float: - s = 0 - for o, weight in parameters: - std2 = 2 * o * o - s += weight / (math.pi * std2) * np.exp(-(x * x + y * y) / std2) - return s - - # First, we need to figure out the kernel size. We'll simply use the - # 2 sigma rule. - kernel_radius = 1 - for o, weight in parameters: - if weight > 0: - kernel_radius = max(kernel_radius, math.ceil(2 * o)) - kernel_radius += 1 - - # Now we can create the kernel. - kernel_size = 2 * kernel_radius + 1 - kernel = np.zeros((kernel_size, kernel_size)) - x_offsets = [0, 0.25, 0.5, 0.75] - for y in range(kernel_size): - y = y - kernel_radius # noqa - for x in range(kernel_size): - x = x - kernel_radius # noqa - # we shift the x value with `abs(x) - 1` to make sure that we sample - # the top of the bell curve. This will give sharper results. - s = 0 - for x_offset in x_offsets: - s += sample(abs(x) - 1 + x_offset, y) - kernel[kernel_radius + y, kernel_radius + x] = ( - s / len(x_offsets) * -np.sign(x) - ) - - return kernel - - -def get_filter_kernels( - edge_filter: EdgeFilter, - gauss_parameter: list[tuple[float, float]], -) -> tuple[np.ndarray, np.ndarray]: - """ - Returns the x and y kernels of the given edge filter. - """ - - filter_x = FILTERS_X.get(edge_filter, None) - if edge_filter == EdgeFilter.MULTI_GAUSS: - filter_x = create_gauss_kernel(gauss_parameter) - assert filter_x is not None, f"Unknown filter '{edge_filter}'" - - if edge_filter != EdgeFilter.MULTI_GAUSS: - # normalize filter - _h, w = filter_x.shape - left = filter_x[:, : w // 2] - filter_x = filter_x / np.sum(left) - - filter_y = np.rot90(filter_x, -1) - return filter_x, filter_y diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/height.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/height.py deleted file mode 100644 index 8a9c745..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/height.py +++ /dev/null @@ -1,55 +0,0 @@ -from enum import Enum - -import numpy as np - -from ...utils.utils import get_h_w_c - - -class HeightSource(Enum): - AVERAGE_RGB = 0 - MAX_RGB = 1 - # 1 - ((1-r) * (1-g) * (1-b)) - SCREEN_RGB = 2 - RED = 3 - GREEN = 4 - BLUE = 5 - ALPHA = 6 - - -def get_height_map(img: np.ndarray, source: HeightSource) -> np.ndarray: - """ - Converts the given color/grayscale image to a height map. - """ - h, w, c = get_h_w_c(img) - - assert c in (1, 3, 4), "Only grayscale, RGB, and RGBA images are supported" - - if source == HeightSource.ALPHA: - if c < 4: - return np.ones((h, w), dtype=np.float32) - return img[:, :, 3] - - if c == 1: - if source == HeightSource.SCREEN_RGB: - x = 1 - img - return 1 - x * x * x - return img - - r = img[:, :, 2] - g = img[:, :, 1] - b = img[:, :, 0] - - if source == HeightSource.RED: - return r - elif source == HeightSource.GREEN: - return g - elif source == HeightSource.BLUE: - return b - elif source == HeightSource.MAX_RGB: - return np.maximum(np.maximum(r, g), b) - elif source == HeightSource.AVERAGE_RGB: - return (r + g + b) / 3 - elif source == HeightSource.SCREEN_RGB: - return 1 - ((1 - r) * (1 - g) * (1 - b)) - else: - raise AssertionError(f"Invalid height source {source}.") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/util.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/util.py deleted file mode 100644 index 4be145a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/normals/util.py +++ /dev/null @@ -1,93 +0,0 @@ -from typing import Tuple - -import numpy as np - -XYZ = Tuple[np.ndarray, np.ndarray, np.ndarray] -""" -The normalized XYZ components of a normal map. Z is guaranteed to be >= 0. -""" - - -def normalize_normals(x: np.ndarray, y: np.ndarray) -> XYZ: - # The square of the length of X and Y - l_sq = np.square(x) + np.square(y) - - # If the length of X and Y is >1, then make it 1 - l = np.sqrt(np.maximum(l_sq, 1)) - x /= l - y /= l - l_sq = np.minimum(l_sq, 1, out=l_sq) - - # Compute Z - z = np.sqrt(1 - l_sq) - - return x, y, z - - -def gr_to_xyz(n: np.ndarray) -> XYZ: - """ - Takes a BGR or BGRA image and converts it into XYZ normal components only by looking at the R and G channels. - """ - - x = n[:, :, 2] * 2 - 1 - y = n[:, :, 1] * 2 - 1 - - return normalize_normals(x, y) - - -def xyz_to_bgr(xyz: XYZ) -> np.ndarray: - """ - Converts the given XYZ components into an BGR image. - """ - x, y, z = xyz - - r = (x + 1) * 0.5 - g = (y + 1) * 0.5 - b = z - - return np.dstack((b, g, r)) - - -def octahedral_gr_to_xyz(n: np.ndarray) -> XYZ: - """ - Takes a BGR or BGRA image of octahedral (RTX Remix) normals and converts it into XYZ normal components only by looking at the R and G channels. - """ - r = n[:, :, 2] * 2 - 1 - g = n[:, :, 1] * 2 - 1 - - x: np.ndarray = (r + g) / 2 - y: np.ndarray = r - x - z: np.ndarray = 1 - np.abs(x) - np.abs(y) - length = np.sqrt(np.square(x) + np.square(y) + np.square(z)) - x /= length - y /= length - z /= length # type: ignore - - return x, y, z - - -def xyz_to_octahedral_bgr(xyz: XYZ) -> np.ndarray: - """ - Converts the given XYZ components into an BGR image with normals using octahedral (RTX Remix) encoding. - - For more information about octahedral normals, see: - https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding/ - https://jcgt.org/published/0003/02/01/ - """ - x, y, z = xyz - absolute = np.abs(x) + np.abs(y) + np.abs(z) - x /= absolute - y /= absolute - - # This is a trick used in RTX Remix to more efficiently encode normals. - # Octahedral normals are defined for the whole range of values (so all possible unit vectors). - # However, we know that we are working with hemispheric normal maps (z>=0) - # and can use this knowledge to remap xy values to assume all possible values in [-1..1]x[-1..1]. - r = x + y - g = x - y - - r = (r + 1) * 0.5 - g = (g + 1) * 0.5 - b = np.zeros(x.shape, dtype=np.float32) - - return np.dstack((b, g, r)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/onnx/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/pytorch/architecture/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/LICENSE.md b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/LICENSE.md deleted file mode 100644 index efe94fb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2020 Daniel Gatis - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/__init__.py deleted file mode 100644 index 0ca44ad..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -Rembg code is modified from and copyright of Daniel Gatis, -and can be found here: https://github.com/danielgatis/rembg -""" diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/bg.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/bg.py deleted file mode 100644 index a55b231..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/bg.py +++ /dev/null @@ -1,122 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np -import onnxruntime as ort -from pymatting import estimate_alpha_cf, estimate_foreground_ml -from scipy.ndimage import binary_erosion - -from ...utils.utils import get_h_w_c -from .session_factory import new_session - - -def assert_rgb(img: np.ndarray): - assert get_h_w_c(img)[2] == 3 - - -def assert_gray(img: np.ndarray): - assert img.ndim == 2 - - -def alpha_matting_cutout( - img: np.ndarray, - mask: np.ndarray, - foreground_threshold: int, - background_threshold: int, - erode_structure_size: int, -) -> np.ndarray: - assert_rgb(img) - assert_gray(mask) - - is_foreground = mask > (foreground_threshold / 255) - is_background = mask < (background_threshold / 255) - - structure = None - if erode_structure_size > 0: - structure = np.ones( - (erode_structure_size, erode_structure_size), dtype=np.uint8 - ) - - is_foreground = binary_erosion(is_foreground, structure=structure) - is_background = binary_erosion(is_background, structure=structure, border_value=1) - - trimap = np.full(mask.shape, dtype=np.float64, fill_value=0.5) - trimap[is_foreground] = 1 - trimap[is_background] = 0 - - img64 = img.astype(np.float64) - alpha = estimate_alpha_cf(img64, trimap) - foreground = estimate_foreground_ml(img64, alpha) - assert isinstance(foreground, np.ndarray) - - return np.dstack((foreground.astype(np.float32), alpha.astype(np.float32))) - - -def naive_cutout(img: np.ndarray, mask: np.ndarray) -> np.ndarray: - assert_rgb(img) - assert_gray(mask) - return np.dstack((img, mask)) - - -def post_process(mask: np.ndarray) -> np.ndarray: - """ - Post Process the mask for a smooth boundary by applying Morphological Operations - Research based on paper: https://www.sciencedirect.com/science/article/pii/S2352914821000757 - args: - mask: Binary Numpy Mask - """ - kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3)) - mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) - mask = cv2.GaussianBlur( - mask, (5, 5), sigmaX=2, sigmaY=2, borderType=cv2.BORDER_DEFAULT - ) - mask = np.where(mask < 0.5, 0, 1).astype(np.float32) - return mask - - -def remove_bg( - img: np.ndarray, - ort_session: ort.InferenceSession, - alpha_matting: bool = False, - alpha_matting_foreground_threshold: int = 240, - alpha_matting_background_threshold: int = 10, - alpha_matting_erode_size: int = 10, - post_process_mask: bool = False, -) -> tuple[np.ndarray, np.ndarray]: - # Flip channels to RGB mode - assert_rgb(img) - img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - session = new_session(ort_session) - - masks: list[np.ndarray] = session.predict(img) - cutouts: list[np.ndarray] = [] - - assert len(masks) > 0, "Model failed to generate masks" - - mask = None - for mask in masks: - if post_process_mask: - mask = post_process(mask) # noqa - - if alpha_matting: - try: - cutout = alpha_matting_cutout( - img, - mask, - alpha_matting_foreground_threshold, - alpha_matting_background_threshold, - alpha_matting_erode_size, - ) - except ValueError: - cutout = naive_cutout(img, mask) - else: - cutout = naive_cutout(img, mask) - - cutouts.append(cutout) - - if mask is None or len(cutouts) == 0: - raise ValueError("Model failed to generate masks") - - cutout = cv2.vconcat(cutouts) - - return cv2.cvtColor(cutout, cv2.COLOR_RGBA2BGRA), mask diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/LICENSE.md b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/LICENSE.md deleted file mode 100644 index 2238d87..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2020 PyMatting - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/__init__.py deleted file mode 100644 index 092174b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -Pymatting code is modified from and copyright of PyMatting, -and can be found here: https://github.com/pymatting/pymatting -""" diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/cf_laplacian.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/cf_laplacian.py deleted file mode 100644 index 4572636..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/cf_laplacian.py +++ /dev/null @@ -1,176 +0,0 @@ -import numpy as np -import scipy.sparse -from numba import njit - -from ....utils.utils import get_h_w_c - - -@njit( - "void(f8[:, :, :], f8, i8, f8[:, :, :], i8[:], i8[:], b1[:, :])", - cache=True, - nogil=True, -) -def _cf_laplacian(image, epsilon, r, values, indices, indptr, is_known): - h, w, d = image.shape - assert d == 3 - size = 2 * r + 1 - window_area = size * size - - for yi in range(h): - for xi in range(w): - i = xi + yi * w - k = i * (4 * r + 1) ** 2 - for yj in range(yi - 2 * r, yi + 2 * r + 1): - for xj in range(xi - 2 * r, xi + 2 * r + 1): - j = xj + yj * w - - if 0 <= xj < w and 0 <= yj < h: - indices[k] = j - - k += 1 - - indptr[i + 1] = k - - # Centered and normalized window colors - c = np.zeros((2 * r + 1, 2 * r + 1, 3)) - - # For each pixel of image - for y in range(r, h - r): - for x in range(r, w - r): - if np.all(is_known[y - r : y + r + 1, x - r : x + r + 1]): - continue - - # For each color channel - for dc in range(3): - # Calculate sum of color channel in window - s = 0.0 - for dy in range(size): - for dx in range(size): - s += image[y + dy - r, x + dx - r, dc] - - # Calculate centered window color - for dy in range(2 * r + 1): - for dx in range(2 * r + 1): - c[dy, dx, dc] = ( - image[y + dy - r, x + dx - r, dc] - s / window_area - ) - - # Calculate covariance matrix over color channels with epsilon regularization - a00 = epsilon - a01 = 0.0 - a02 = 0.0 - a11 = epsilon - a12 = 0.0 - a22 = epsilon - - for dy in range(size): - for dx in range(size): - a00 += c[dy, dx, 0] * c[dy, dx, 0] - a01 += c[dy, dx, 0] * c[dy, dx, 1] - a02 += c[dy, dx, 0] * c[dy, dx, 2] - a11 += c[dy, dx, 1] * c[dy, dx, 1] - a12 += c[dy, dx, 1] * c[dy, dx, 2] - a22 += c[dy, dx, 2] * c[dy, dx, 2] - - a00 /= window_area - a01 /= window_area - a02 /= window_area - a11 /= window_area - a12 /= window_area - a22 /= window_area - - det = ( - a00 * a12 * a12 - + a01 * a01 * a22 - + a02 * a02 * a11 - - a00 * a11 * a22 - - 2 * a01 * a02 * a12 - ) - - inv_det = 1.0 / det - - # Calculate inverse covariance matrix - m00 = (a12 * a12 - a11 * a22) * inv_det - m01 = (a01 * a22 - a02 * a12) * inv_det - m02 = (a02 * a11 - a01 * a12) * inv_det - m11 = (a02 * a02 - a00 * a22) * inv_det - m12 = (a00 * a12 - a01 * a02) * inv_det - m22 = (a01 * a01 - a00 * a11) * inv_det - - # For each pair ((xi, yi), (xj, yj)) in a (2 r + 1)x(2 r + 1) window - for dyi in range(2 * r + 1): - for dxi in range(2 * r + 1): - s = c[dyi, dxi, 0] - t = c[dyi, dxi, 1] - u = c[dyi, dxi, 2] - - c0 = m00 * s + m01 * t + m02 * u - c1 = m01 * s + m11 * t + m12 * u - c2 = m02 * s + m12 * t + m22 * u - - for dyj in range(2 * r + 1): - for dxj in range(2 * r + 1): - xi = x + dxi - r - yi = y + dyi - r - xj = x + dxj - r - yj = y + dyj - r - - i = xi + yi * w - j = xj + yj * w - - # Calculate contribution of pixel pair to L_ij - temp = ( - c0 * c[dyj, dxj, 0] - + c1 * c[dyj, dxj, 1] - + c2 * c[dyj, dxj, 2] - ) - - value = (1.0 if (i == j) else 0.0) - ( - 1 + temp - ) / window_area - - dx = xj - xi + 2 * r - dy = yj - yi + 2 * r - - values[i, dy, dx] += value - - -def cf_laplacian(image, epsilon=1e-7, radius=1, is_known=None): - """ - This function implements the alpha estimator for closed-form alpha matting as proposed by :cite:`levin2007closed`. - Parameters - ------------ - image: numpy.ndarray - Image with shape :math:`h\\times w \\times 3` - epsilon: float - Regularization strength, defaults to :math:`10^{-7}`. Strong regularization improves convergence but results in smoother alpha mattes. - radius: int - Radius of local window size, defaults to :math:`1`, i.e. only adjacent pixels are considered. - The size of the local window is given as :math:`(2 r + 1)^2`, where :math:`r` denotes the radius. A larger radius might lead to violated color line constraints, but also - favors further propagation of information within the image. - is_known: numpy.ndarray - Binary mask of pixels for which to compute the laplacian matrix. - Laplacian entries for known pixels will have undefined values. - Returns - ------- - L: scipy.sparse.spmatrix - Matting Laplacian - """ - h, w, _ = get_h_w_c(image) - n = h * w - - if is_known is None: - is_known = np.zeros((h, w), dtype=np.bool_) - - is_known = is_known.reshape(h, w) - - # Data for matting laplacian in csr format - indptr = np.zeros(n + 1, dtype=np.int64) - indices = np.zeros(n * (4 * radius + 1) ** 2, dtype=np.int64) - values = np.zeros((n, 4 * radius + 1, 4 * radius + 1), dtype=np.float64) - - _cf_laplacian(image, epsilon, radius, values, indices, indptr, is_known) - - L = scipy.sparse.csr_matrix((values.ravel(), indices, indptr), (n, n)) - - return L diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/cg.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/cg.py deleted file mode 100644 index 0ce16ef..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/cg.py +++ /dev/null @@ -1,117 +0,0 @@ -import numpy as np - - -def cg( - A, - b, - x0=None, - atol=0.0, - rtol=1e-7, - maxiter=10000, - callback=None, - M=None, - reorthogonalize=False, -): - """Solves a system of linear equations :math:`Ax=b` using conjugate gradient descent :cite:`hestenes1952methods` - - Parameters - ---------- - A: scipy.sparse.csr_matrix - Square matrix - b: numpy.ndarray - Vector describing the right-hand side of the system - x0: numpy.ndarray - Initialization, if `None` then :code:`x=np.zeros_like(b)` - atol: float - Absolute tolerance. The loop terminates if the :math:`||r||` is smaller than `atol`, where :math:`r` denotes the residual of the current iterate. - rtol: float - Relative tolerance. The loop terminates if :math:`{||r||}/{||b||}` is smaller than `rtol`, where :math:`r` denotes the residual of the current iterate. - callback: function - Function :code:`callback(A, x, b, norm_b, r, norm_r)` called after each iteration, defaults to `None` - M: function or scipy.sparse.csr_matrix - Function that applies the preconditioner to a vector. Alternatively, `M` can be a matrix describing the precondioner. - reorthogonalize: boolean - Wether to apply reorthogonalization of the residuals after each update, defaults to `False` - - - Returns - ------- - x: numpy.ndarray - Solution of the system - - Example - ------- - >>> from pymatting import * - >>> import numpy as np - >>> A = np.array([[3.0, 1.0], [1.0, 2.0]]) - >>> M = jacobi(A) - >>> b = np.array([4.0, 3.0]) - >>> cg(A, b, M=M) - array([1., 1.]) - """ - if M is None: - - def precondition(x): - return x - - elif callable(M): - precondition = M - else: - - def precondition(x): - return M.dot(x) - - x = np.zeros_like(b) if x0 is None else x0.copy() - - norm_b = np.linalg.norm(b) - - if callable(A): - r = b - A(x) - else: - r = b - A.dot(x) - - norm_r = np.linalg.norm(r) - - if norm_r < atol or norm_r < rtol * norm_b: - return x - - z = precondition(r) - p = z.copy() - rz = np.inner(r, z) - - for _ in range(maxiter): - r_old = r.copy() - - if callable(A): - Ap = A(p) - else: - Ap = A.dot(p) - - alpha = rz / np.inner(p, Ap) - x += alpha * p - r -= alpha * Ap - - norm_r = np.linalg.norm(r) - - if callback is not None: - callback(A, x, b, norm_b, r, norm_r) - - if norm_r < atol or norm_r < rtol * norm_b: - return x - - z = precondition(r) - - if reorthogonalize: - beta = np.inner(r - r_old, z) / rz - rz = np.inner(r, z) - else: - beta = 1.0 / rz - rz = np.inner(r, z) - beta *= rz - - p *= beta - p += z - - raise ValueError( - "Conjugate gradient descent did not converge within %d iterations" % maxiter - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/estimate_alpha_cf.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/estimate_alpha_cf.py deleted file mode 100644 index 1828e28..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/estimate_alpha_cf.py +++ /dev/null @@ -1,77 +0,0 @@ -import numpy as np - -from .cf_laplacian import cf_laplacian -from .cg import cg -from .ichol import ichol -from .util import sanity_check_image, trimap_split - - -def estimate_alpha_cf( # pylint: disable=dangerous-default-value - image, trimap, preconditioner=None, laplacian_kwargs={}, cg_kwargs={} -): - """ - Estimate alpha from an input image and an input trimap using Closed-Form Alpha Matting as proposed by :cite:`levin2007closed`. - - Parameters - ---------- - image: numpy.ndarray - Image with shape :math:`h \\times w \\times d` for which the alpha matte should be estimated - trimap: numpy.ndarray - Trimap with shape :math:`h \\times w` of the image - preconditioner: function or scipy.sparse.linalg.LinearOperator - Function or sparse matrix that applies the preconditioner to a vector (default: ichol) - laplacian_kwargs: dictionary - Arguments passed to the :code:`cf_laplacian` function - cg_kwargs: dictionary - Arguments passed to the :code:`cg` solver - is_known: numpy.ndarray - Binary mask of pixels for which to compute the laplacian matrix. - Providing this parameter might improve performance if few pixels are unknown. - - Returns - ------- - alpha: numpy.ndarray - Estimated alpha matte - - Example - ------- - >>> from pymatting import * - >>> image = load_image("data/lemur/lemur.png", "RGB") - >>> trimap = load_image("data/lemur/lemur_trimap.png", "GRAY") - >>> alpha = estimate_alpha_cf( - ... image, - ... trimap, - ... laplacian_kwargs={"epsilon": 1e-6}, - ... cg_kwargs={"maxiter":2000}) - """ - if preconditioner is None: - preconditioner = ichol - - sanity_check_image(image) - - is_fg, _, is_known, is_unknown = trimap_split(trimap) - - L = cf_laplacian(image, **laplacian_kwargs, is_known=is_known) - - # Split Laplacian matrix L into - # - # [L_U R ] - # [R^T L_K] - # - # and then solve L_U x_U = -R is_fg_K for x where K (is_known) corresponds to - # fixed pixels and U (is_unknown) corresponds to unknown pixels. For reference, see - # Grady, Leo, et al. "Random walks for interactive alpha-matting." Proceedings of VIIP. Vol. 2005. 2005. - - L_U = L[is_unknown, :][:, is_unknown] - - R = L[is_unknown, :][:, is_known] - - m = is_fg[is_known] - - x = trimap.copy().ravel() - - x[is_unknown] = cg(L_U, -R.dot(m), M=preconditioner(L_U), **cg_kwargs) - - alpha = np.clip(x, 0, 1).reshape(trimap.shape) - - return alpha diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/estimate_foreground_ml.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/estimate_foreground_ml.py deleted file mode 100644 index 62af16a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/estimate_foreground_ml.py +++ /dev/null @@ -1,242 +0,0 @@ -import numpy as np -from numba import njit, prange - - -@njit("void(f4[:, :, :], f4[:, :, :])", cache=True, nogil=True, parallel=True) -def _resize_nearest_multichannel(dst, src): - """ - Internal method. - - Resize image src to dst using nearest neighbors filtering. - Images must have multiple color channels, i.e. :code:`len(shape) == 3`. - - Parameters - ---------- - dst: numpy.ndarray of type np.float32 - output image - src: numpy.ndarray of type np.float32 - input image - """ - h_src, w_src, depth = src.shape - h_dst, w_dst, depth = dst.shape - - for y_dst in prange(h_dst): # pylint: disable=not-an-iterable - for x_dst in range(w_dst): - x_src = max(0, min(w_src - 1, x_dst * w_src // w_dst)) - y_src = max(0, min(h_src - 1, y_dst * h_src // h_dst)) - - for c in range(depth): - dst[y_dst, x_dst, c] = src[y_src, x_src, c] - - -@njit("void(f4[:, :], f4[:, :])", cache=True, nogil=True, parallel=True) -def _resize_nearest(dst, src): - """ - Internal method. - - Resize image src to dst using nearest neighbors filtering. - Images must be grayscale, i.e. :code:`len(shape) == 3`. - - Parameters - ---------- - dst: numpy.ndarray of type np.float32 - output image - src: numpy.ndarray of type np.float32 - input image - """ - h_src, w_src = src.shape - h_dst, w_dst = dst.shape - - for y_dst in prange(h_dst): # pylint: disable=not-an-iterable - for x_dst in range(w_dst): - x_src = max(0, min(w_src - 1, x_dst * w_src // w_dst)) - y_src = max(0, min(h_src - 1, y_dst * h_src // h_dst)) - - dst[y_dst, x_dst] = src[y_src, x_src] - - -# TODO -# There should be an option to switch @njit(parallel=True) on or off. -# parallel=True would be faster, but might cause race conditions. -# User should have the option to turn it on or off. -@njit( - "Tuple((f4[:, :, :], f4[:, :, :]))(f4[:, :, :], f4[:, :], f4, i4, i4, i4, f4)", - cache=True, - nogil=True, -) -def _estimate_fb_ml( - input_image, - input_alpha, - regularization, - n_small_iterations, - n_big_iterations, - small_size, - gradient_weight, -): - h0, w0, depth = input_image.shape - - dtype = np.float32 - - w_prev = 1 - h_prev = 1 - - F_prev = np.empty((h_prev, w_prev, depth), dtype=dtype) - B_prev = np.empty((h_prev, w_prev, depth), dtype=dtype) - - n_levels = int(np.ceil(np.log2(max(w0, h0)))) - - F = B = None - for i_level in range(n_levels + 1): - w = round(w0 ** (i_level / n_levels)) - h = round(h0 ** (i_level / n_levels)) - - image = np.empty((h, w, depth), dtype=dtype) - alpha = np.empty((h, w), dtype=dtype) - - _resize_nearest_multichannel(image, input_image) - _resize_nearest(alpha, input_alpha) - - F = np.empty((h, w, depth), dtype=dtype) - B = np.empty((h, w, depth), dtype=dtype) - - _resize_nearest_multichannel(F, F_prev) - _resize_nearest_multichannel(B, B_prev) - - if w <= small_size and h <= small_size: - n_iter = n_small_iterations - else: - n_iter = n_big_iterations - - b = np.zeros((2, depth), dtype=dtype) - - dx = [-1, 1, 0, 0] - dy = [0, 0, -1, 1] - - for _ in range(n_iter): - for y in prange(h): # pylint: disable=not-an-iterable - for x in range(w): - a0 = alpha[y, x] - a1 = 1.0 - a0 - - a00 = a0 * a0 - a01 = a0 * a1 - # a10 = a01 can be omitted due to symmetry of matrix - a11 = a1 * a1 - - for c in range(depth): - b[0, c] = a0 * image[y, x, c] - b[1, c] = a1 * image[y, x, c] - - for d in range(4): - x2 = max(0, min(w - 1, x + dx[d])) - y2 = max(0, min(h - 1, y + dy[d])) - - gradient = abs(a0 - alpha[y2, x2]) - - da = regularization + gradient_weight * gradient - - a00 += da - a11 += da - - for c in range(depth): - b[0, c] += da * F[y2, x2, c] - b[1, c] += da * B[y2, x2, c] - - determinant = a00 * a11 - a01 * a01 - - inv_det = 1.0 / determinant - - b00 = inv_det * a11 - b01 = inv_det * -a01 - b11 = inv_det * a00 - - for c in range(depth): - F_c = b00 * b[0, c] + b01 * b[1, c] - B_c = b01 * b[0, c] + b11 * b[1, c] - - F_c = max(0.0, min(1.0, F_c)) - B_c = max(0.0, min(1.0, B_c)) - - F[y, x, c] = F_c - B[y, x, c] = B_c - - F_prev = F - B_prev = B - - w_prev = w - h_prev = h - - if F is None or B is None: - raise ValueError("Input image does not have multiple levels.") - - return F, B - - -def estimate_foreground_ml( - image, - alpha, - regularization=1e-5, - n_small_iterations=10, - n_big_iterations=2, - small_size=32, - return_background=False, - gradient_weight=1.0, -): - """Estimates the foreground of an image given its alpha matte. - - See :cite:`germer2020multilevel` for reference. - - Parameters - ---------- - image: numpy.ndarray - Input image with shape :math:`h \\times w \\times d` - alpha: numpy.ndarray - Input alpha matte shape :math:`h \\times w` - regularization: float - Regularization strength :math:`\\epsilon`, defaults to :math:`10^{-5}`. - Higher regularization results in smoother colors. - n_small_iterations: int - Number of iterations performed on small scale, defaults to :math:`10` - n_big_iterations: int - Number of iterations performed on large scale, defaults to :math:`2` - small_size: int - Threshold that determines at which size `n_small_iterations` should be used - return_background: bool - Whether to return the estimated background in addition to the foreground - gradient_weight: float - Larger values enforce smoother foregrounds, defaults to :math:`1` - - Returns - ------- - F: numpy.ndarray - Extracted foreground - B: numpy.ndarray - Extracted background - - Example - ------- - >>> from pymatting import * - >>> image = load_image("data/lemur/lemur.png", "RGB") - >>> alpha = load_image("data/lemur/lemur_alpha.png", "GRAY") - >>> F = estimate_foreground_ml(image, alpha, return_background=False) - >>> F, B = estimate_foreground_ml(image, alpha, return_background=True) - - See Also - ---- - stack_images: This function can be used to place the foreground on a new background. - """ - - foreground, background = _estimate_fb_ml( - image.astype(np.float32), - alpha.astype(np.float32), - regularization, - n_small_iterations, - n_big_iterations, - small_size, - gradient_weight, - ) - - if return_background: - return foreground, background - - return foreground diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/ichol.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/ichol.py deleted file mode 100644 index 246fa2c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/ichol.py +++ /dev/null @@ -1,307 +0,0 @@ -import numpy as np -import scipy.sparse -from numba import njit - - -@njit( - "i8(i8, f8[:], i8[:], i8[:], f8[:], i8[:], i8[:], f8, f8, i8, f8, b1)", - cache=True, - nogil=True, -) -def _ichol( - n, - Av, - Ar, - Ap, - Lv, - Lr, - Lp, - discard_threshold, - shift, - max_nnz, - relative_discard_threshold, - diag_keep_discarded, -): - """ - :cite:`jones1995improved` might be slightly interesting for the general idea - to use linked list to keep track of the sparse matrix values. But instead of - pointers, we have to use indices here, since this is Python and not C. - """ - nnz = 0 - c_n = 0 - s = np.zeros(n, np.int64) # Next non-zero row index i in column j of L - t = np.zeros(n, np.int64) # First subdiagonal index i in column j of A - l = ( - np.zeros(n, np.int64) - 1 - ) # Linked list of non-zero columns in row k of L; type: ignore - a = np.zeros(n, np.float64) # Values of column j - r = np.zeros(n, np.float64) # r[j] = sum(abs(A[j:, j])) for relative threshold - b = np.zeros( - n, np.bool_ - ) # b[i] indicates if the i-th element of column j is non-zero - c = np.empty(n, np.int64) # Row indices of non-zero elements in column j - d = np.full(n, shift, np.float64) # Diagonal elements of A - for j in range(n): - for idx in range(Ap[j], Ap[j + 1]): - i = Ar[idx] - if i == j: - d[j] += Av[idx] - t[j] = idx + 1 - if i >= j: - r[j] += abs(Av[idx]) - for j in range(n): # For each column j - for idx in range(t[j], Ap[j + 1]): # For each L_ij - i = Ar[idx] - L_ij = Av[idx] - if L_ij != 0.0 and i > j: - a[i] += L_ij # Assign non-zero value to L_ij in sparse column - if not b[i]: - b[i] = True # Mark it as non-zero - c[c_n] = i # Remember index for later deletion - c_n += 1 - k = l[j] # Find index k of column with non-zero element in row j - while k != -1: # For each column of that type - k0 = s[k] # Start index of non-zero elements in column k - k1 = Lp[k + 1] # End index - k2 = l[k] # Remember next column index before it is overwritten - L_jk = Lv[k0] # Value of non-zero element at start of column - k0 += 1 # Advance to next non-zero element in column - if k0 < k1: # If there is a next non-zero element - s[k] = k0 # Advance start index in column k to next non-zero element - i = Lr[k0] # Row index of next non-zero element in column k - l[k] = l[i] # Remember old list i index in list k - l[i] = k # Insert index of non-zero element into list i - for idx in range(k0, k1): # For each non-zero L_ik in column k - i = Lr[idx] - L_ik = Lv[idx] - a[i] -= L_ik * L_jk # Update element L_ij in sparse column - if not b[i]: # Check if sparse column element was zero - b[i] = True # Mark as non-zero in sparse column - c[c_n] = i # Remember index for later deletion - c_n += 1 - k = k2 # Advance to next column k - if d[j] <= 0.0: - return -1 - if nnz + 1 + c_n > max_nnz: - return -2 - d[j] = np.sqrt(d[j]) # Update diagonal element L_ii - Lv[nnz] = d[j] # Add diagonal element L_ii to L - Lr[nnz] = j # Add row index of L_ii to L - nnz += 1 - s[j] = nnz # Set first non-zero index of column j - for i in np.sort( - c[:c_n] - ): # Sort row indices of column j for correct insertion order into L - L_ij = a[i] / d[j] # Get non-zero element from sparse column j - if diag_keep_discarded: - d[i] -= L_ij * L_ij # Update diagonal element L_ii - rel = ( - relative_discard_threshold * r[j] - ) # Relative discard threshold (before div) - if ( - abs(L_ij) > discard_threshold and abs(a[i]) > rel - ): # If element is sufficiently non-zero - if not diag_keep_discarded: - d[i] -= L_ij * L_ij # Update diagonal element L_ii - Lv[nnz] = L_ij # Add element L_ij to L - Lr[nnz] = i # Add row index of L_ij - nnz += 1 - a[i] = 0.0 # Set element i in column j to zero - b[i] = False # Mark element as zero - c_n = 0 # Discard row indices of non-zero elements in column j. - Lp[j + 1] = nnz # Update count of non-zero elements up to column j - if Lp[j] + 1 < Lp[j + 1]: # If column j has a non-zero element below diagonal - i = Lr[Lp[j] + 1] # Row index of first off-diagonal non-zero element - l[j] = l[i] # Remember old list i index in list j - l[i] = j # Insert index of non-zero element into list i - return nnz - - -@njit("void(f8[:], i8[:], i8[:], f8[:], i8)", cache=True, nogil=True) -def _backsub_L_csc_inplace(data, indices, indptr, x, n): - for j in range(n): - k = indptr[j] - L_jj = data[k] - temp = x[j] / L_jj - - x[j] = temp - - for k in range(indptr[j] + 1, indptr[j + 1]): - i = indices[k] - L_ij = data[k] - - x[i] -= L_ij * temp - - -@njit("void(f8[:], i8[:], i8[:], f8[:], i8)", cache=True, nogil=True) -def _backsub_LT_csc_inplace(data, indices, indptr, x, n): - for i in range(n - 1, -1, -1): - s = x[i] - - for k in range(indptr[i] + 1, indptr[i + 1]): - j = indices[k] - L_ji = data[k] - s -= L_ji * x[j] - - k = indptr[i] - L_ii = data[k] - - x[i] = s / L_ii - - -class CholeskyDecomposition(object): - """Cholesky Decomposition - - Calling this object applies the preconditioner to a vector by forward and back substitution. - - Parameters - ---------- - Ltuple: tuple of numpy.ndarrays - Tuple of array describing values, row indices and row pointers for Cholesky factor in the compressed sparse column format (csc) - """ - - def __init__(self, Ltuple): - self.Ltuple = Ltuple - - @property - def L(self): - """Returns the Cholesky factor - - Returns - ------- - L: scipy.sparse.csc_matrix - Cholesky factor - """ - _, _, Lp = self.Ltuple - n = len(Lp) - 1 - return scipy.sparse.csc_matrix(self.Ltuple, (n, n)) - - def __call__(self, b): - Lv, Lr, Lp = self.Ltuple - n = len(b) - x = b.copy() - _backsub_L_csc_inplace(Lv, Lr, Lp, x, n) - _backsub_LT_csc_inplace(Lv, Lr, Lp, x, n) - return x - - -def ichol( # pylint: disable=dangerous-default-value - A, - discard_threshold=1e-4, - shifts=[0.0, 1e-4, 1e-3, 1e-2, 0.1, 0.5, 1.0, 10.0, 100, 1e3, 1e4, 1e5], - max_nnz=int(4e9 / 16), - relative_discard_threshold=0.0, - diag_keep_discarded=True, -): - """Implements the thresholded incomplete Cholesky decomposition - - Parameters - ---------- - A: scipy.sparse.csc_matrix - Matrix for which the preconditioner should be calculated - discard_threshold: float - Values having an absolute value smaller than this threshold will be discarded while calculating the Cholesky decompositions - shifts: array of floats - Values to try for regularizing the matrix of interest in case it is not positive definite after discarding the small values - max_nnz: int - Maximum number of non-zero entries in the Cholesky decomposition. Defaults to 250 million, which should usually be around 4 GB. - relative_discard_threshold: float - Values with an absolute value of less than :code:`relative_discard_threshold * sum(abs(A[j:, j]))` will be discarded. - A dense ichol implementation with relative threshold would look like this:: - - L = np.tril(A) - for j in range(n): - col = L[j:, j] - col -= np.sum(L[j, :j] * L[j:, :j], axis=1) - discard_mask = abs(col[1:]) < relative_discard_threshold * np.sum(np.abs(A[j:, j])) - col[1:][discard_mask] = 0 - col[0] **= 0.5 - col[1:] /= col[0] - - diag_keep_discarded: bool - Whether to update the diagonal with the discarded values. Usually better if :code:`True`. - - Returns - ------- - chol: CholeskyDecomposition - Preconditioner or solver object. - - Raises - ------ - ValueError - If inappropriate parameter values were passed - - Example - ------- - >>> from pymatting import * - >>> import numpy as np - >>> from scipy.sparse import csc_matrix - >>> A = np.array([[2.0, 3.0], [3.0, 5.0]]) - >>> cholesky_decomposition = ichol(csc_matrix(A)) - >>> cholesky_decomposition(np.array([1.0, 2.0])) - array([-1., 1.]) - """ - - if isinstance(A, scipy.sparse.csr_matrix): - A = A.T - - if not isinstance(A, scipy.sparse.csc_matrix): - raise ValueError("Matrix A must be a scipy.sparse.csc_matrix") - - if not A.has_canonical_format: - A.sum_duplicates() - - m, n = A.shape - - assert m == n - - Lv = np.empty(max_nnz, dtype=np.float64) # Values of non-zero elements of L - Lr = np.empty(max_nnz, dtype=np.int64) # Row indices of non-zero elements of L - Lp = np.zeros( - n + 1, dtype=np.int64 - ) # Start(Lp[i]) and end(Lp[i+1]) index of L[:, i] in Lv - - nnz = -3 - for shift in shifts: - nnz = _ichol( - n, - A.data, - A.indices.astype(np.int64), - A.indptr.astype(np.int64), - Lv, - Lr, - Lp, - discard_threshold, - shift, - max_nnz, - relative_discard_threshold, - diag_keep_discarded, - ) - - if nnz >= 0: - break - - if nnz == -1: - print("PERFORMANCE WARNING:") - print( - "Thresholded incomplete Cholesky decomposition failed due to insufficient positive-definiteness of matrix A with parameters:" - ) - print(" discard_threshold = %e" % discard_threshold) - print(" shift = %e" % shift) - print("Try decreasing discard_threshold or start with a larger shift") - print("") - - if nnz == -2: - raise ValueError( - "Thresholded incomplete Cholesky decomposition failed because more than max_nnz non-zero elements were created. Try increasing max_nnz or discard_threshold." - ) - - if nnz < 0: - raise ValueError( - "Thresholded incomplete Cholesky decomposition failed due to insufficient positive-definiteness of matrix A and diagonal shifts did not help." - ) - - Lv = Lv[:nnz] - Lr = Lr[:nnz] - - return CholeskyDecomposition((Lv, Lr, Lp)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/util.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/util.py deleted file mode 100644 index cd24568..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/pymatting/util.py +++ /dev/null @@ -1,163 +0,0 @@ -import warnings - -import numpy as np - - -def sanity_check_image(image): - """Performs a sanity check for input images. Image values should be in the - range [0, 1], the `dtype` should be `np.float32` or `np.float64` and the - image shape should be `(?, ?, 3)`. - Parameters - ---------- - image: numpy.ndarray - Image with shape :math:`h \\times w \\times 3` - Example - ------- - >>> import numpy as np - >>> from pymatting import check_image - >>> image = (np.random.randn(64, 64, 2) * 255).astype(np.int32) - >>> sanity_check_image(image) - __main__:1: UserWarning: Expected RGB image of shape (?, ?, 3), but image.shape is (64, 64, 2). - __main__:1: UserWarning: Image values should be in [0, 1], but image.min() is -933. - __main__:1: UserWarning: Image values should be in [0, 1], but image.max() is 999. - __main__:1: UserWarning: Unexpected image.dtype int32. Are you sure that you do not want to use np.float32 or np.float64 instead? - """ - - if len(image.shape) != 3 or image.shape[2] != 3: - warnings.warn( - "Expected RGB image of shape (?, ?, 3), but image.shape is %s." - % str(image.shape), - stacklevel=3, - ) - - min_value = image.min() - max_value = image.max() - - if min_value < 0.0: - warnings.warn( - "Image values should be in [0, 1], but image.min() is %s." % min_value, - stacklevel=3, - ) - - if max_value > 1.0: - warnings.warn( - "Image values should be in [0, 1], but image.max() is %s." % max_value, - stacklevel=3, - ) - - if image.dtype not in [np.float32, np.float64]: - warnings.warn( - "Unexpected image.dtype %s. Are you sure that you do not want to use np.float32 or np.float64 instead?" - % image.dtype, - stacklevel=3, - ) - - -def stack_images(*images): - """This function stacks images along the third axis. - This is useful for combining e.g. rgb color channels or color and alpha channels. - Parameters - ---------- - *images: numpy.ndarray - Images to be stacked. - Returns - ------- - image: numpy.ndarray - Stacked images as numpy.ndarray - Example - ------- - >>> from pymatting.util.util import stack_images - >>> import numpy as np - >>> I = stack_images(np.random.rand(4,5,3), np.random.rand(4,5,3)) - >>> I.shape - (4, 5, 6) - """ - images = [ - (image if len(image.shape) == 3 else image[:, :, np.newaxis]) - for image in images - ] - return np.concatenate(images, axis=2) - - -def trimap_split(trimap, flatten=True, bg_threshold=0.1, fg_threshold=0.9): - """This function splits the trimap into foreground pixels, background pixels, and unknown pixels. - Foreground pixels are pixels where the trimap has values larger than or equal to `fg_threshold` (default: 0.9). - Background pixels are pixels where the trimap has values smaller than or equal to `bg_threshold` (default: 0.1). - Pixels with other values are assumed to be unknown. - Parameters - ---------- - trimap: numpy.ndarray - Trimap with shape :math:`h \\times w` - flatten: bool - If true np.flatten is called on the trimap - Returns - ------- - is_fg: numpy.ndarray - Boolean array indicating which pixel belongs to the foreground - is_bg: numpy.ndarray - Boolean array indicating which pixel belongs to the background - is_known: numpy.ndarray - Boolean array indicating which pixel is known - is_unknown: numpy.ndarray - Boolean array indicating which pixel is unknown - bg_threshold: float - Pixels with smaller trimap values will be considered background. - fg_threshold: float - Pixels with larger trimap values will be considered foreground. - Example - ------- - >>> import numpy as np - >>> from pymatting import * - >>> trimap = np.array([[1,0],[0.5,0.2]]) - >>> is_fg, is_bg, is_known, is_unknown = trimap_split(trimap) - >>> is_fg - array([ True, False, False, False]) - >>> is_bg - array([False, True, False, False]) - >>> is_known - array([ True, True, False, False]) - >>> is_unknown - array([False, False, True, True]) - """ - if flatten: - trimap = trimap.flatten() - - min_value = trimap.min() - max_value = trimap.max() - - if min_value < 0.0: - warnings.warn( - "Trimap values should be in [0, 1], but trimap.min() is %s." % min_value, - stacklevel=3, - ) - - if max_value > 1.0: - warnings.warn( - "Trimap values should be in [0, 1], but trimap.max() is %s." % min_value, - stacklevel=3, - ) - - if trimap.dtype not in [np.float32, np.float64]: - warnings.warn( - "Unexpected trimap.dtype %s. Are you sure that you do not want to use np.float32 or np.float64 instead?" - % trimap.dtype, - stacklevel=3, - ) - - is_fg = trimap >= fg_threshold - is_bg = trimap <= bg_threshold - - if is_bg.sum() == 0: - raise ValueError( - "Trimap did not contain background values (values <= %f)" % bg_threshold - ) - - if is_fg.sum() == 0: - raise ValueError( - "Trimap did not contain foreground values (values >= %f)" % fg_threshold - ) - - is_known = is_fg | is_bg - is_unknown = ~is_known - - return is_fg, is_bg, is_known, is_unknown diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_base.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_base.py deleted file mode 100644 index 8019fd3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_base.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod - -import numpy as np -import onnxruntime as ort - -from nodes.impl.resize import ResizeFilter, resize - - -class BaseSession(ABC): - def __init__( - self, - inner_session: ort.InferenceSession, - mean: tuple[float, float, float], - std: tuple[float, float, float], - size: tuple[int, int], - ): - self.inner_session = inner_session - self.mean = mean - self.std = std - self.size = size - - def normalize(self, img: np.ndarray) -> dict[str, np.ndarray]: - img = resize(img, self.size, ResizeFilter.LANCZOS) - - tmp_img = np.zeros((img.shape[0], img.shape[1], 3)) - tmp_img[:, :, 0] = (img[:, :, 0] - self.mean[0]) / self.std[0] - tmp_img[:, :, 1] = (img[:, :, 1] - self.mean[1]) / self.std[1] - tmp_img[:, :, 2] = (img[:, :, 2] - self.mean[2]) / self.std[2] - - tmp_img = tmp_img.transpose((2, 0, 1)) - - model_input_name = self.inner_session.get_inputs()[0].name - - return {model_input_name: np.expand_dims(tmp_img, 0).astype(np.float32)} - - @abstractmethod - def predict(self, img: np.ndarray) -> list[np.ndarray]: - pass diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_cloth.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_cloth.py deleted file mode 100644 index cbc0f42..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_cloth.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -import numpy as np -from PIL import Image -from scipy.special import log_softmax - -from nodes.impl.image_utils import normalize -from nodes.utils.utils import get_h_w_c - -from .session_base import BaseSession - -pallete1 = [ - 0, - 0, - 0, - 255, - 255, - 255, - 0, - 0, - 0, - 0, - 0, - 0, -] - -pallete2 = [ - 0, - 0, - 0, - 0, - 0, - 0, - 255, - 255, - 255, - 0, - 0, - 0, -] - -pallete3 = [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 255, - 255, - 255, -] - - -class ClothSession(BaseSession): - def predict(self, img: np.ndarray) -> list[np.ndarray]: - h, w, _ = get_h_w_c(img) - ort_outs = self.inner_session.run(None, self.normalize(img)) - - pred = ort_outs - pred = log_softmax(pred[0], 1) - pred = np.argmax(pred, axis=1, keepdims=True) - pred = np.squeeze(pred, 0) - pred = np.squeeze(pred, 0) - - mask = Image.fromarray(pred.astype("uint8"), mode="L") - mask = mask.resize((w, h), Image.LANCZOS) - - masks: list[np.ndarray] = [] - - mask1 = mask.copy() - mask1.putpalette(pallete1) - mask1 = mask1.convert("RGB").convert("L") - masks.append(normalize(np.array(mask1))) - - mask2 = mask.copy() - mask2.putpalette(pallete2) - mask2 = mask2.convert("RGB").convert("L") - masks.append(normalize(np.array(mask2))) - - mask3 = mask.copy() - mask3.putpalette(pallete3) - mask3 = mask3.convert("RGB").convert("L") - masks.append(normalize(np.array(mask3))) - - return masks diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_factory.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_factory.py deleted file mode 100644 index 93f1708..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_factory.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -import onnxruntime as ort - -from ...impl.onnx.utils import get_input_shape -from .session_base import BaseSession -from .session_cloth import ClothSession -from .session_simple import SimpleSession - - -def new_session(session: ort.InferenceSession) -> BaseSession: - session_class: type[BaseSession] - - input_width = get_input_shape(session)[2] - - # Using size to determine session type and norm parameters is fragile, - # but at the moment I don't know a better way to detect architecture due - # to the lack of consistency in naming and outputs across arches and repos. - # It works right now because of the limited number of models supported, - # but if that expands, it may become necessary to find an alternative. - mean = (0.485, 0.456, 0.406) - std = (0.229, 0.224, 0.225) - size = (input_width, input_width) if input_width is not None else (320, 320) - if input_width == 768: # U2NET cloth model - session_class = ClothSession - mean = (0.5, 0.5, 0.5) - std = (0.5, 0.5, 0.5) - else: - session_class = SimpleSession - if input_width == 1024: # ISNET - mean = (0.5, 0.5, 0.5) - std = (1, 1, 1) - elif input_width == 512: # Models trained using anime-segmentation repo - mean = (0, 0, 0) - std = (1, 1, 1) - - return session_class(session, mean, std, size) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_simple.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_simple.py deleted file mode 100644 index 2dbc99b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/rembg/session_simple.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.impl.image_utils import normalize -from nodes.impl.resize import ResizeFilter, resize -from nodes.utils.utils import get_h_w_c - -from .session_base import BaseSession - - -class SimpleSession(BaseSession): - def predict(self, img: np.ndarray) -> list[np.ndarray]: - h, w, _ = get_h_w_c(img) - ort_outs = self.inner_session.run(None, self.normalize(img)) - - pred = ort_outs[0][:, 0, :, :] - - ma = np.max(pred) - mi = np.min(pred) - - pred = (pred - mi) / (ma - mi) - mask = normalize(np.squeeze(pred)) - mask = np.squeeze(resize(mask, (w, h), ResizeFilter.LANCZOS)) - - return [mask] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/tile.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/tile.py deleted file mode 100644 index aab899d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/tile.py +++ /dev/null @@ -1,40 +0,0 @@ -import math -from enum import Enum - -import cv2 -import numpy as np - -from ..utils.utils import get_h_w_c - - -class TileMode(Enum): - TILE = 0 - MIRROR = 1 - - -def tile_image(img: np.ndarray, width: int, height: int, mode: TileMode) -> np.ndarray: - if mode == TileMode.TILE: - # do nothing - pass - elif mode == TileMode.MIRROR: - # flip the image to create a mirrored tile - flip_x: np.ndarray = cv2.flip(img, 0) - flip_y: np.ndarray = cv2.flip(img, 1) - flip_xy: np.ndarray = cv2.flip(img, -1) - - img = cv2.vconcat( - [ - cv2.hconcat([img, flip_y]), # type: ignore - cv2.hconcat([flip_x, flip_xy]), # type: ignore - ] - ) - else: - raise AssertionError(f"Invalid tile mode {mode}") - - h, w, _ = get_h_w_c(img) - tile_w = math.ceil(width / w) - tile_h = math.ceil(height / h) - img = np.tile(img, (tile_h, tile_w) if img.ndim == 2 else (tile_h, tile_w, 1)) - - # crop to make sure the dimensions are correct - return img[:height, :width] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/upscale/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/video.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/video.py deleted file mode 100644 index 39981b5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/impl/video.py +++ /dev/null @@ -1,109 +0,0 @@ -import subprocess -from dataclasses import dataclass -from io import BufferedIOBase -from pathlib import Path - -import ffmpeg -import numpy as np -from sanic.log import logger - -from .ffmpeg import FFMpegEnv - - -@dataclass(frozen=True) -class VideoMetadata: - width: int - height: int - fps: float - frame_count: int - - @staticmethod - def from_file(path: Path, ffmpeg_env: FFMpegEnv): - probe = ffmpeg.probe(path, cmd=ffmpeg_env.ffprobe) - video_format = probe.get("format", None) - if video_format is None: - raise RuntimeError("Failed to get video format. Please report.") - video_stream = next( - (stream for stream in probe["streams"] if stream["codec_type"] == "video"), - None, - ) - - if video_stream is None: - raise RuntimeError("No video stream found in file") - - width = video_stream.get("width", None) - if width is None: - raise RuntimeError("No width found in video stream") - width = int(width) - - height = video_stream.get("height", None) - if height is None: - raise RuntimeError("No height found in video stream") - height = int(height) - - fps = video_stream.get("r_frame_rate", None) - if fps is None: - raise RuntimeError("No fps found in video stream") - fps = int(fps.split("/")[0]) / int(fps.split("/")[1]) - - frame_count = video_stream.get("nb_frames", None) - if frame_count is None: - duration = video_stream.get("duration", None) - if duration is None: - duration = video_format.get("duration", None) - if duration is not None: - frame_count = float(duration) * fps - else: - raise RuntimeError( - "No frame count or duration found in video stream. Unable to determine video length. Please report." - ) - frame_count = int(frame_count) - - return VideoMetadata( - width=width, - height=height, - fps=fps, - frame_count=frame_count, - ) - - -class VideoLoader: - def __init__(self, path: Path, ffmpeg_env: FFMpegEnv): - self.path = path - self.ffmpeg_env = ffmpeg_env - self.metadata = VideoMetadata.from_file(path, ffmpeg_env) - - def get_audio_stream(self): - return ffmpeg.input(self.path).audio - - def stream_frames(self): - """ - Returns an iterator that yields frames as BGR uint8 numpy arrays. - """ - - ffmpeg_reader = ( - ffmpeg.input(self.path) - .output( - "pipe:", - format="rawvideo", - pix_fmt="bgr24", - sws_flags="lanczos+accurate_rnd+full_chroma_int+full_chroma_inp+bitexact", - loglevel="error", - ) - .run_async(pipe_stdout=True, pipe_stderr=False, cmd=self.ffmpeg_env.ffmpeg) - ) - assert isinstance(ffmpeg_reader, subprocess.Popen) - - with ffmpeg_reader: - assert isinstance(ffmpeg_reader.stdout, BufferedIOBase) - - width = self.metadata.width - height = self.metadata.height - - while True: - in_bytes = ffmpeg_reader.stdout.read(width * height * 3) - if not in_bytes: - logger.debug("Can't receive frame (stream end?). Exiting ...") - break - - yield np.frombuffer(in_bytes, np.uint8).reshape([height, width, 3]) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/__system_inputs.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/__system_inputs.py deleted file mode 100644 index 7454ce4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/__system_inputs.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -import math -from typing import Literal - -from api import BaseInput -from navi import ExpressionJson - - -class StaticValueInput(BaseInput): - def __init__( - self, - label: str, - py_type: type = str, - navi_type: ExpressionJson = "string", - value: Literal["execution_number"] = "execution_number", - ): - super().__init__(navi_type, label, kind="static", has_handle=False) - - self.associated_type = py_type - self.value = value - - def to_dict(self): - return { - **super().to_dict(), - "value": self.value, - } - - def enforce(self, value: object): - return_value = value - if not isinstance(value, self.associated_type): - return_value = self.associated_type(value) - - if isinstance(value, (float, int)) and math.isnan(value): - raise ValueError("NaN is not a valid number") - - return return_value diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/ncnn_inputs.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/ncnn_inputs.py deleted file mode 100644 index c5b2e7c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/ncnn_inputs.py +++ /dev/null @@ -1,11 +0,0 @@ -from api import BaseInput - -from ...impl.ncnn.model import NcnnModelWrapper - - -class NcnnModelInput(BaseInput): - """Input for NcnnModel""" - - def __init__(self, label: str = "Model"): - super().__init__("NcnnNetwork", label) - self.associated_type = NcnnModelWrapper diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/onnx_inputs.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/onnx_inputs.py deleted file mode 100644 index 8437236..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/inputs/onnx_inputs.py +++ /dev/null @@ -1,63 +0,0 @@ -import navi -from api import BaseInput - -from ...impl.onnx.model import OnnxModel, OnnxModels, OnnxRemBg, is_rembg_model -from .generic_inputs import DropDownInput - - -class OnnxModelInput(BaseInput): - """Input for onnx model""" - - def __init__( - self, label: str = "Model", input_type: navi.ExpressionJson = "OnnxModel" - ): - super().__init__(input_type, label) - self.associated_type = OnnxModel - - -class OnnxGenericModelInput(OnnxModelInput): - """ONNX model input for things that aren't background removal""" - - def __init__( - self, label: str = "Model", input_type: navi.ExpressionJson = "OnnxModel" - ): - super().__init__(label, navi.intersect(input_type, "OnnxGenericModel")) - - def enforce(self, value: object): - assert isinstance(value, OnnxModels) - assert not is_rembg_model(value.bytes), "Expected a non-rembg model" - return value - - -class OnnxRemBgModelInput(OnnxModelInput): - """ONNX model input for background removal""" - - def __init__( - self, label: str = "Model", input_type: navi.ExpressionJson = "OnnxModel" - ): - super().__init__(label, navi.intersect(input_type, "OnnxRemBgModel")) - self.associated_type = OnnxRemBg - - def enforce(self, value: object): - assert isinstance(value, OnnxModels) - assert is_rembg_model(value.bytes), "Expected a rembg model" - return value - - -def OnnxFpDropdown() -> DropDownInput: - return DropDownInput( - input_type="FpMode", - label="Data Type", - options=[ - { - "option": "fp32", - "value": 0, - "type": "FpMode::fp32", - }, - { - "option": "fp16", - "value": 1, - "type": "FpMode::fp16", - }, - ], - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/ncnn_outputs.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/ncnn_outputs.py deleted file mode 100644 index 3e48ac5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/ncnn_outputs.py +++ /dev/null @@ -1,36 +0,0 @@ -import navi -from api import BaseOutput, OutputKind - -from ...impl.ncnn.model import NcnnModelWrapper -from ...utils.format import format_channel_numbers - - -class NcnnModelOutput(BaseOutput): - def __init__( - self, - model_type: navi.ExpressionJson = "NcnnNetwork", - label: str = "Model", - kind: OutputKind = "generic", - ): - super().__init__(model_type, label, kind=kind, associated_type=NcnnModelWrapper) - - def get_broadcast_data(self, value: NcnnModelWrapper): - return { - "tags": [ - format_channel_numbers(value.in_nc, value.out_nc), - f"{value.nf}nf", - value.fp, - ] - } - - def get_broadcast_type(self, value: NcnnModelWrapper): - return navi.named( - "NcnnNetwork", - { - "scale": value.scale, - "inputChannels": value.in_nc, - "outputChannels": value.out_nc, - "nf": value.nf, - "fp": navi.literal(value.fp), - }, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/onnx_outputs.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/onnx_outputs.py deleted file mode 100644 index 17cf8fa..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/properties/outputs/onnx_outputs.py +++ /dev/null @@ -1,27 +0,0 @@ -import navi -from api import BaseOutput - -from ...impl.onnx.model import OnnxModel - - -class OnnxModelOutput(BaseOutput): - """Output for onnx model""" - - def __init__( - self, - model_type: navi.ExpressionJson = "OnnxModel", - label: str = "Model", - ): - super().__init__(model_type, label, associated_type=OnnxModel) - - def get_broadcast_type(self, value: OnnxModel): - fields = { - "subType": navi.literal(value.sub_type), - } - - if value.scale_width: - fields["scaleWidth"] = value.scale_width - if value.scale_height: - fields["scaleHeight"] = value.scale_height - - return navi.named("OnnxModel", fields) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/checked_cast.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/checked_cast.py deleted file mode 100644 index c74e6dc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/checked_cast.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import annotations - -from typing import TypeVar - -T = TypeVar("T") - - -def checked_cast(t: type[T], value: object) -> T: - assert isinstance(value, t), f"Value is {type(value)}, must be type {t}" - return value diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/exec_options.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/exec_options.py deleted file mode 100644 index 0534aa3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/exec_options.py +++ /dev/null @@ -1,39 +0,0 @@ -from __future__ import annotations - -from typing import Dict - -from sanic.log import logger - -SettingsJson = Dict[str, object] -JsonExecutionOptions = Dict[str, SettingsJson] - - -class ExecutionOptions: - def __init__( - self, - backend_settings: JsonExecutionOptions, - ) -> None: - self.__settings = backend_settings - - logger.info(f"Execution options: {self.__settings}") - - @staticmethod - def parse(json: JsonExecutionOptions) -> ExecutionOptions: - return ExecutionOptions(backend_settings=json) - - def get_package_settings(self, package_id: str) -> SettingsJson: - return self.__settings.get(package_id, {}) - - -__global_exec_options = ExecutionOptions({}) - - -def get_execution_options() -> ExecutionOptions: - return __global_exec_options - - -def set_execution_options(value: ExecutionOptions): - # TODO: Make the mutable global state unnecessary - # pylint: disable=global-statement - global __global_exec_options - __global_exec_options = value diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/replacement.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/replacement.py deleted file mode 100644 index 8d17fc3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/replacement.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import annotations - -import re - - -class ReplacementInterpolation: - def __init__(self, name: str): - self.name = name - - -class ReplacementString: - """ - A parser and interpolator for chainner's string replacement patterns. - - The syntax is as follows (ANTLR 4): - - Pattern: ( LiteralChar | EscapedChar | Interpolation )* ; - LiteralChar: ~[{] ; // any character except "{" - EscapedChar: '{{' ; - Interpolation: '{' InterpolationContent '}' ; - InterpolationContent: [A-Za-z0-9]+ ; - """ - - def __init__(self, pattern: str): - self.tokens: list[str | ReplacementInterpolation] = [] - self.names: set[str] = set() - - content_pattern = re.compile(r"\A\w+\Z") - - last_index = 0 - last_str = "" - for m in re.compile(r"(\{\{)|\{([^{}]*)\}").finditer(pattern): - last_str += pattern[last_index : m.start()] - last_index = m.end() - - interpolation = m.group(2) - if interpolation is not None: - if interpolation == "": - raise ValueError( - "Invalid replacement pattern. {} is not a valid replacement." - """ Either specify a name or id number, or escape a single "{" as "{{".""" - f" Full pattern: {pattern}" - ) - if content_pattern.fullmatch(interpolation) is None: - raise ValueError( - "Invalid replacement pattern." - f" {{{interpolation}}} is not a valid replacement." - " Names and ids only allow letters and digits." - f" Full pattern: {pattern}" - ) - - self.tokens.append(last_str) - last_str = "" - self.tokens.append(ReplacementInterpolation(interpolation)) - self.names.add(interpolation) - else: - last_str += "{" - last_str += pattern[last_index:] - self.tokens.append(last_str) - - def replace(self, replacements: dict[str, str]) -> str: - result = "" - - for token in self.tokens: - if isinstance(token, str): - result += token - elif token.name in replacements: - result += replacements[token.name] - else: - raise ValueError( - "Unknown replacement." - f" There is no replacement with the name or id {token.name}." - f" Available replacements: {', '.join(replacements.keys()) }." - ) - - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/unpickler.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/unpickler.py deleted file mode 100644 index 90a3766..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/nodes/utils/unpickler.py +++ /dev/null @@ -1,30 +0,0 @@ -# Safe unpickler to prevent arbitrary code execution -import pickle -from types import SimpleNamespace - -safe_list = { - ("collections", "OrderedDict"), - ("typing", "OrderedDict"), - ("torch._utils", "_rebuild_tensor_v2"), - ("torch", "BFloat16Storage"), - ("torch", "FloatStorage"), - ("torch", "HalfStorage"), - ("torch", "IntStorage"), - ("torch", "LongStorage"), - ("torch", "DoubleStorage"), -} - - -class RestrictedUnpickler(pickle.Unpickler): - def find_class(self, module: str, name: str): - # Only allow required classes to load state dict - if (module, name) not in safe_list: - raise pickle.UnpicklingError(f"Global '{module}.{name}' is forbidden") - return super().find_class(module, name) - - -RestrictedUnpickle = SimpleNamespace( - Unpickler=RestrictedUnpickler, - __name__="pickle", - load=lambda *args, **kwargs: RestrictedUnpickler(*args, **kwargs).load(), -) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/__init__.py deleted file mode 100644 index a99b85f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/__init__.py +++ /dev/null @@ -1,40 +0,0 @@ -import sys - -from sanic.log import logger - -from api import add_package - -package = add_package( - __file__, - id="chaiNNer_external", - name="External", - description="Interact with an external Stable Diffusion API", -) - -external_stable_diffusion_category = package.add_category( - name="Stable Diffusion (External)", - description="Interact with an external Stable Diffusion API", - icon="FaPaintBrush", - color="#9331CC", -) - -_FEATURE_DESCRIPTION = f""" -ChaiNNer can connect to [AUTOMATIC1111's Stable Diffusion Web UI](https://github.com/AUTOMATIC1111/stable-diffusion-webui) to run Stable Diffusion nodes. - -If you want to use the External Stable Diffusion nodes, run the Automatic1111 web UI with the `--api` flag, like so: - -```bash -./webui.{"bat" if sys.platform == "win32" else "sh"} --api -``` - -To manually set where chaiNNer looks for the API, use the `STABLE_DIFFUSION_PROTOCOL`, `STABLE_DIFFUSION_HOST`, and `STABLE_DIFFUSION_PORT` environment variables. By default, `127.0.0.1` will be the host. If not specified, chaiNNer will try to auto-detect the protocol and port. -""" - - -web_ui_feature_descriptor = package.add_feature( - id="webui", - name="AUTOMATIC1111/stable-diffusion-webui", - description=_FEATURE_DESCRIPTION, -) - -logger.debug(f"Loaded package {package.name}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/__init__.py deleted file mode 100644 index fb25687..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .. import external_stable_diffusion_category - -auto1111_group = external_stable_diffusion_category.add_node_group("Automatic1111") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/clip_interrogate.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/clip_interrogate.py deleted file mode 100644 index f94af65..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/clip_interrogate.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.node_cache import cached -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import TextOutput - -from ...features import web_ui -from ...util import encode_base64_image -from ...web_ui import STABLE_DIFFUSION_INTERROGATE_PATH, get_api -from .. import auto1111_group - - -@auto1111_group.register( - schema_id="chainner:external_stable_diffusion:interrograte", - name="CLIP Interrogate", - description="Use Automatic1111 to get a description of an image", - icon="MdTextFields", - inputs=[ - ImageInput(), - ], - outputs=[ - TextOutput("Text"), - ], - decorators=[cached], - features=web_ui, -) -def clip_interrogate_node(image: np.ndarray) -> str: - request_data = { - "image": encode_base64_image(image), - } - response = get_api().post( - path=STABLE_DIFFUSION_INTERROGATE_PATH, json_data=request_data - ) - return response["caption"] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/image_to_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/image_to_image.py deleted file mode 100644 index c2a2214..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/image_to_image.py +++ /dev/null @@ -1,142 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.groups import seed_group -from nodes.node_cache import cached -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - ImageInput, - SeedInput, - SliderInput, - TextInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.seed import Seed -from nodes.utils.utils import get_h_w_c - -from ...features import web_ui -from ...util import decode_base64_image, encode_base64_image, nearest_valid_size -from ...web_ui import ( - RESIZE_MODE_LABELS, - SAMPLER_NAME_LABELS, - STABLE_DIFFUSION_IMG2IMG_PATH, - ResizeMode, - SamplerName, - get_api, -) -from .. import auto1111_group - - -@auto1111_group.register( - schema_id="chainner:external_stable_diffusion:img2img", - name="Image to Image", - description="Modify an image using Automatic1111", - icon="MdChangeCircle", - inputs=[ - ImageInput(), - TextInput("Prompt", multiline=True).make_optional(), - TextInput("Negative Prompt", multiline=True).make_optional(), - SliderInput( - "Denoising Strength", - minimum=0, - default=0.75, - maximum=1, - slider_step=0.01, - controls_step=0.1, - precision=2, - ), - seed_group(SeedInput()), - SliderInput("Steps", minimum=1, default=20, maximum=150), - EnumInput( - SamplerName, - default=SamplerName.EULER, - option_labels=SAMPLER_NAME_LABELS, - ), - SliderInput( - "CFG Scale", - minimum=1, - default=7, - maximum=20, - controls_step=0.1, - precision=1, - ), - EnumInput( - ResizeMode, - default=ResizeMode.JUST_RESIZE, - option_labels=RESIZE_MODE_LABELS, - ).with_id(10), - SliderInput( - "Width", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ).with_id(8), - SliderInput( - "Height", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ).with_id(9), - BoolInput("Seamless Edges", default=False), - ], - outputs=[ - ImageOutput( - image_type=""" - def nearest_valid(n: number) = floor(n / 8) * 8; - Image { - width: nearest_valid(Input8), - height: nearest_valid(Input9) - }""", - channels=3, - ), - ], - decorators=[cached], - features=web_ui, - limited_to_8bpc=True, -) -def image_to_image_node( - image: np.ndarray, - prompt: str | None, - negative_prompt: str | None, - denoising_strength: float, - seed: Seed, - steps: int, - sampler_name: SamplerName, - cfg_scale: float, - resize_mode: ResizeMode, - width: int, - height: int, - tiling: bool, -) -> np.ndarray: - width, height = nearest_valid_size( - width, height - ) # This cooperates with the "image_type" of the ImageOutput - request_data = { - "init_images": [encode_base64_image(image)], - "prompt": prompt or "", - "negative_prompt": negative_prompt or "", - "denoising_strength": denoising_strength, - "seed": seed.to_u32(), - "steps": steps, - "sampler_name": sampler_name.value, - "cfg_scale": cfg_scale, - "width": width, - "height": height, - "resize_mode": resize_mode.value, - "tiling": tiling, - } - response = get_api().post( - path=STABLE_DIFFUSION_IMG2IMG_PATH, json_data=request_data - ) - result = decode_base64_image(response["images"][0]) - h, w, _ = get_h_w_c(result) - assert ( - (w, h) == (width, height) - ), f"Expected the returned image to be {width}x{height}px but found {w}x{h}px instead " - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/inpaint.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/inpaint.py deleted file mode 100644 index 023aa23..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/inpaint.py +++ /dev/null @@ -1,188 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -import navi -from nodes.groups import if_enum_group, seed_group -from nodes.node_cache import cached -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - ImageInput, - SeedInput, - SliderInput, - TextInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.seed import Seed -from nodes.utils.utils import get_h_w_c - -from ...features import web_ui -from ...util import decode_base64_image, encode_base64_image, nearest_valid_size -from ...web_ui import ( - RESIZE_MODE_LABELS, - SAMPLER_NAME_LABELS, - STABLE_DIFFUSION_IMG2IMG_PATH, - InpaintingFill, - ResizeMode, - SamplerName, - get_api, -) -from .. import auto1111_group - - -class InpaintArea(Enum): - WHOLE_PICTURE = "WholePicture" - ONLY_MASKED = "OnlyMasked" - - -@auto1111_group.register( - schema_id="chainner:external_stable_diffusion:img2img_inpainting", - name="Inpaint", - description="Modify a masked part of an image using Automatic1111", - icon="MdChangeCircle", - inputs=[ - ImageInput(), - ImageInput("Mask", channels=1, image_type=navi.Image(size_as="Input0")), - TextInput("Prompt", multiline=True).make_optional(), - TextInput("Negative Prompt", multiline=True).make_optional(), - SliderInput( - "Denoising Strength", - minimum=0, - default=0.75, - maximum=1, - slider_step=0.01, - controls_step=0.1, - precision=2, - ), - seed_group(SeedInput()), - SliderInput("Steps", minimum=1, default=20, maximum=150), - EnumInput( - SamplerName, - default=SamplerName.EULER, - option_labels=SAMPLER_NAME_LABELS, - ), - SliderInput( - "CFG Scale", - minimum=1, - default=7, - maximum=20, - controls_step=0.1, - precision=1, - ), - EnumInput( - ResizeMode, - default=ResizeMode.JUST_RESIZE, - option_labels=RESIZE_MODE_LABELS, - ), - SliderInput( - "Width", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ), - SliderInput( - "Height", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ), - BoolInput("Seamless Edges", default=False), - SliderInput( - "Mask Blur", - minimum=0, - default=4, - maximum=64, - unit="px", - ), - EnumInput(InpaintingFill, default=InpaintingFill.ORIGINAL), - EnumInput(InpaintArea, default=InpaintArea.WHOLE_PICTURE), - if_enum_group(15, InpaintArea.ONLY_MASKED)( - SliderInput( - "Only masked padding", - minimum=0, - default=32, - maximum=256, - slider_step=4, - controls_step=4, - unit="px", - ), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - def nearest_valid(n: number) = floor(n / 8) * 8; - Image { - width: if Input15==InpaintArea::OnlyMasked {Input0.width} else {nearest_valid(Input10)}, - height: if Input15==InpaintArea::OnlyMasked {Input0.height} else {nearest_valid(Input11)} - }""", - channels=3, - ), - ], - decorators=[cached], - features=web_ui, - limited_to_8bpc=True, -) -def inpaint_node( - image: np.ndarray, - mask: np.ndarray, - prompt: str | None, - negative_prompt: str | None, - denoising_strength: float, - seed: Seed, - steps: int, - sampler_name: SamplerName, - cfg_scale: float, - resize_mode: ResizeMode, - width: int, - height: int, - tiling: bool, - mask_blur: int, - inpainting_fill: InpaintingFill, - inpaint_area: InpaintArea, - inpaint_full_res_padding: int, -) -> np.ndarray: - width, height = nearest_valid_size( - width, height - ) # This cooperates with the "image_type" of the ImageOutput - request_data = { - "init_images": [encode_base64_image(image)], - "mask": encode_base64_image(mask), - "inpainting_fill": inpainting_fill.value, - "mask_blur": mask_blur, - "inpaint_full_res": inpaint_area == InpaintArea.ONLY_MASKED, - "inpaint_full_res_padding": inpaint_full_res_padding, - "prompt": prompt or "", - "negative_prompt": negative_prompt or "", - "denoising_strength": denoising_strength, - "seed": seed.to_u32(), - "steps": steps, - "sampler_name": sampler_name.value, - "cfg_scale": cfg_scale, - "width": width, - "height": height, - "resize_mode": resize_mode.value, - "tiling": tiling, - } - response = get_api().post( - path=STABLE_DIFFUSION_IMG2IMG_PATH, json_data=request_data - ) - result = decode_base64_image(response["images"][0]) - h, w, _ = get_h_w_c(result) - if inpaint_area == InpaintArea.ONLY_MASKED: - in_h, in_w, _ = get_h_w_c(image) - assert ( - (w, h) == (in_w, in_h) - ), f"Expected the returned image to be {in_w}x{in_h}px but found {w}x{h}px instead " - else: - assert ( - (w, h) == (width, height) - ), f"Expected the returned image to be {width}x{height}px but found {w}x{h}px instead " - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/outpaint.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/outpaint.py deleted file mode 100644 index 8b94c16..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/outpaint.py +++ /dev/null @@ -1,262 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from math import ceil -from typing import Any - -import numpy as np - -from nodes.groups import if_enum_group, seed_group -from nodes.node_cache import cached -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - ImageInput, - SeedInput, - SliderInput, - TextInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.seed import Seed -from nodes.utils.utils import get_h_w_c - -from ...features import web_ui -from ...util import decode_base64_image, encode_base64_image, nearest_valid_size -from ...web_ui import ( - RESIZE_MODE_LABELS, - SAMPLER_NAME_LABELS, - STABLE_DIFFUSION_IMG2IMG_PATH, - InpaintingFill, - ResizeMode, - SamplerName, - get_api, -) -from .. import auto1111_group - - -class OutpaintingMethod(Enum): - POOR_MAN_OUTPAINTING = 0 - OUTPAINTING_MK2 = 1 - - -@auto1111_group.register( - schema_id="chainner:external_stable_diffusion:img2img_outpainting", - name="Outpaint", - description='Outpaint an image using the "Poor man\'s outpainting" script from Automatic1111', - icon="MdChangeCircle", - inputs=[ - ImageInput().with_id(0), - TextInput("Prompt", multiline=True).make_optional(), - TextInput("Negative Prompt", multiline=True).make_optional(), - SliderInput( - "Denoising Strength", - minimum=0, - default=0.75, - maximum=1, - slider_step=0.01, - controls_step=0.1, - precision=2, - ), - seed_group(SeedInput()), - SliderInput("Steps", minimum=1, default=20, maximum=150), - EnumInput( - SamplerName, - default=SamplerName.EULER, - option_labels=SAMPLER_NAME_LABELS, - ), - SliderInput( - "CFG Scale", - minimum=1, - default=7, - maximum=20, - controls_step=0.1, - precision=1, - ), - EnumInput( - ResizeMode, - default=ResizeMode.JUST_RESIZE, - option_labels=RESIZE_MODE_LABELS, - ), - SliderInput( - "Tile Width", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ), - SliderInput( - "Tile Height", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ), - SliderInput( - "Pixels to Expand", - minimum=8, - default=128, - maximum=256, - slider_step=8, - controls_step=8, - ).with_id(11), - SliderInput( - "Mask Blur", - minimum=0, - default=4, - maximum=64, - ), - BoolInput("Extend Left", default=True).with_id(13), - BoolInput("Extend Right", default=True).with_id(14), - BoolInput("Extend Up", default=True).with_id(15), - BoolInput("Extend Down", default=True).with_id(16), - EnumInput( - OutpaintingMethod, default=OutpaintingMethod.POOR_MAN_OUTPAINTING - ).with_id(17), - if_enum_group(17, OutpaintingMethod.POOR_MAN_OUTPAINTING)( - EnumInput(InpaintingFill, default=InpaintingFill.FILL), - ), - if_enum_group(17, OutpaintingMethod.OUTPAINTING_MK2)( - SliderInput( - "Fall-off Exponent (lower=higher detail)", - minimum=0, - default=1, - maximum=4, - precision=2, - slider_step=0.01, - controls_step=0.01, - ), - SliderInput( - "Color Variation", - minimum=0, - default=0.05, - maximum=1, - precision=2, - slider_step=0.01, - controls_step=0.01, - ), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - def nearest_valid(n: number) = ceil(n / 64) * 64; - Image { - width: nearest_valid( - Input0.width - + if Input13 { Input11 } else { 0 } - + if Input14 { Input11 } else { 0 } - ), - height: nearest_valid( - Input0.height - + if Input15 { Input11 } else { 0 } - + if Input16 { Input11 } else { 0 } - ), - }""", - channels=3, - ), - ], - decorators=[cached], - features=web_ui, - limited_to_8bpc=True, -) -def outpaint_node( - image: np.ndarray, - prompt: str | None, - negative_prompt: str | None, - denoising_strength: float, - seed: Seed, - steps: int, - sampler_name: SamplerName, - cfg_scale: float, - resize_mode: ResizeMode, - width: int, - height: int, - pixels_to_expand: int, - mask_blur: int, - extend_left: bool, - extend_right: bool, - extend_up: bool, - extend_down: bool, - outpainting_method: OutpaintingMethod, - inpainting_fill: InpaintingFill, - falloff_exponent: float, - color_variation: float, -) -> np.ndarray: - width, height = nearest_valid_size(width, height) - - expected_output_height, expected_output_width, _ = get_h_w_c(image) - - direction = [] - if extend_left: - direction.append("left") - expected_output_width += pixels_to_expand - if extend_right: - direction.append("right") - expected_output_width += pixels_to_expand - if extend_up: - direction.append("up") - expected_output_height += pixels_to_expand - if extend_down: - direction.append("down") - expected_output_height += pixels_to_expand - - expected_output_width = int(ceil(expected_output_width / 64) * 64) - expected_output_height = int(ceil(expected_output_height / 64) * 64) - - direction = ",".join(direction) - request_data: dict[str, Any] = { - "init_images": [encode_base64_image(image)], - "prompt": prompt or "", - "negative_prompt": negative_prompt or "", - "denoising_strength": denoising_strength, - "seed": seed.to_u32(), - "steps": steps, - "sampler_name": sampler_name.value, - "cfg_scale": cfg_scale, - "width": width, - "height": height, - "resize_mode": resize_mode.value, - } - if outpainting_method == OutpaintingMethod.POOR_MAN_OUTPAINTING: - request_data.update( - { - "script_name": "Poor man's outpainting", - "script_args": list( - { - "pixels": pixels_to_expand, - "mask_blur": mask_blur, - "inpainting_fill": inpainting_fill.value, - "direction": direction, - }.values() - ), - } - ) - - if outpainting_method == OutpaintingMethod.OUTPAINTING_MK2: - request_data.update( - { - "script_name": "Outpainting MK2", - "script_args": list( - { - "_": "", - "pixels": pixels_to_expand, - "mask_blur": mask_blur, - "direction": direction, - "noise_q": falloff_exponent, - "color_variation": color_variation, - }.values() - ), - } - ) - - response = get_api().post( - path=STABLE_DIFFUSION_IMG2IMG_PATH, json_data=request_data - ) - result = decode_base64_image(response["images"][0]) - h, w, _ = get_h_w_c(result) - assert ( - (w, h) == (expected_output_width, expected_output_height) - ), f"Expected the returned image to be {expected_output_width}x{expected_output_height}px but found {w}x{h}px instead " - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/text_to_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/text_to_image.py deleted file mode 100644 index 8c46d4c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/text_to_image.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.groups import seed_group -from nodes.node_cache import cached -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - SeedInput, - SliderInput, - TextInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.seed import Seed -from nodes.utils.utils import get_h_w_c - -from ...features import web_ui -from ...util import decode_base64_image, nearest_valid_size -from ...web_ui import ( - SAMPLER_NAME_LABELS, - STABLE_DIFFUSION_TEXT2IMG_PATH, - SamplerName, - get_api, -) -from .. import auto1111_group - - -@auto1111_group.register( - "chainner:external_stable_diffusion:txt2img", - name="Text to Image", - description="Generate an image using Automatic1111", - icon="BsFillImageFill", - inputs=[ - TextInput("Prompt", multiline=True).make_optional(), - TextInput("Negative Prompt", multiline=True).make_optional(), - seed_group(SeedInput()), - SliderInput("Steps", minimum=1, default=20, maximum=150), - EnumInput( - SamplerName, - default=SamplerName.EULER, - option_labels=SAMPLER_NAME_LABELS, - ), - SliderInput( - "CFG Scale", - minimum=1, - default=7, - maximum=20, - controls_step=0.1, - precision=1, - ), - SliderInput( - "Width", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ).with_id(6), - SliderInput( - "Height", - minimum=64, - default=512, - maximum=2048, - slider_step=8, - controls_step=8, - ).with_id(7), - BoolInput("Seamless Edges", default=False), - ], - outputs=[ - ImageOutput( - image_type=""" - def nearest_valid(n: number) = floor(n / 8) * 8; - Image { - width: nearest_valid(Input6), - height: nearest_valid(Input7) - }""", - channels=3, - ), - ], - decorators=[cached], - features=web_ui, -) -def text_to_image_node( - prompt: str | None, - negative_prompt: str | None, - seed: Seed, - steps: int, - sampler_name: SamplerName, - cfg_scale: float, - width: int, - height: int, - tiling: bool, -) -> np.ndarray: - # This cooperates with the "image_type" of the ImageOutput - width, height = nearest_valid_size(width, height) - request_data = { - "prompt": prompt or "", - "negative_prompt": negative_prompt or "", - "seed": seed.to_u32(), - "steps": steps, - "sampler_name": sampler_name.value, - "cfg_scale": cfg_scale, - "width": width, - "height": height, - "tiling": tiling, - } - response = get_api().post( - path=STABLE_DIFFUSION_TEXT2IMG_PATH, json_data=request_data - ) - result = decode_base64_image(response["images"][0]) - h, w, _ = get_h_w_c(result) - assert ( - (w, h) == (width, height) - ), f"Expected the returned image to be {width}x{height}px but found {w}x{h}px instead " - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/upscale.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/upscale.py deleted file mode 100644 index 881d404..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/external_stable_diffusion/automatic1111/upscale.py +++ /dev/null @@ -1,198 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import Condition, if_enum_group, if_group -from nodes.node_cache import cached -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - ImageInput, - NumberInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from ...features import web_ui -from ...util import decode_base64_image, encode_base64_image -from ...web_ui import ( - STABLE_DIFFUSION_EXTRA_SINGLE_IMAGE_PATH, - UPSCALE_NAME_LABELS, - UpscalerName, - get_api, -) -from .. import auto1111_group - - -class UpscalerMode(Enum): - SCALE_BY = "ScaleBy" - SCALE_TO = "ScaleTo" - - -UPSCALER_MODE_LABELS = { - UpscalerMode.SCALE_BY: "Scale by", - UpscalerMode.SCALE_TO: "Scale to", -} - - -@auto1111_group.register( - schema_id="chainner:external_stable_diffusion:upscaling", - name="Upscale", - description="Upscale an image using Automatic1111", - icon="MdChangeCircle", - inputs=[ - ImageInput(channels=3), - EnumInput( - UpscalerMode, - default=UpscalerMode.SCALE_BY, - option_labels=UPSCALER_MODE_LABELS, - ).with_id(1), - if_enum_group(1, UpscalerMode.SCALE_BY)( - SliderInput( - "Resize multiplier", - minimum=1.0, - default=4.0, - maximum=8.0, - slider_step=0.1, - controls_step=0.1, - precision=1, - ).with_id(2), - ), - if_enum_group(1, UpscalerMode.SCALE_TO)( - NumberInput("Width", controls_step=1, minimum=1, default=512).with_id(3), - NumberInput("Height", controls_step=1, minimum=1, default=512).with_id(4), - BoolInput("Crop to fit", default=True).with_id(5), - ), - EnumInput( - UpscalerName, - label="Upscaler 1", - option_labels=UPSCALE_NAME_LABELS, - ), - BoolInput("Use second upscaler", default=False).with_id(7), - if_group(Condition.bool(7, True))( - EnumInput( - UpscalerName, - label="Upscaler 2", - option_labels=UPSCALE_NAME_LABELS, - ), - SliderInput( - "Upscaler 2 visibility", - minimum=0.0, - default=0.0, - maximum=1.0, - slider_step=0.001, - controls_step=0.001, - precision=3, - ), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - def nearest_valid(n: number) = max(1, floor(n)); - - let in_w = Input0.width; - let in_h = Input0.height; - let ratio_w = width / in_w; - let ratio_h = height / in_h; - let larger_ratio = max(ratio_w, ratio_h); - - let mode = Input1; - let factor = Input2; - - let crop = Input5; - let width = Input3; - let height = Input4; - - match mode { - UpscalerMode::ScaleTo => if crop { - Image { width, height } - } else { - Image { - width: nearest_valid(in_w*larger_ratio), - height: nearest_valid(in_h*larger_ratio) - } - }, - UpscalerMode::ScaleBy => Image { - width: nearest_valid(in_w*factor), - height: nearest_valid(in_h*factor) - } - } - """, - channels=3, - ) - ], - decorators=[cached], - features=web_ui, - limited_to_8bpc=True, -) -def upscale_node( - image: np.ndarray, - mode: UpscalerMode, - upscaling_resize: float, - width: int, - height: int, - crop: bool, - upscaler_1: UpscalerName, - use_second_upscaler: bool, - upscaler_2: UpscalerName, - upscaler_2_visibility: float, -) -> np.ndarray: - if mode == UpscalerMode.SCALE_BY: - resize_mode = 0 - else: - resize_mode = 1 - - if use_second_upscaler: - u2 = upscaler_2.value - else: - u2 = "None" - - request_data = { - "resize_mode": resize_mode, - "show_extras_results": False, - "gfpgan_visibility": 0.0, - "codeformer_visibility": 0.0, - "codeformer_weight": 0.0, - "upscaling_resize": upscaling_resize, - "upscaling_resize_w": width, - "upscaling_resize_h": height, - "upscaling_crop": crop, - "upscaler_1": upscaler_1.value, - "upscaler_2": u2, - "extras_upscaler_2_visibility": upscaler_2_visibility, - "upscale_first": False, - "image": encode_base64_image(image), - } - response = get_api().post( - path=STABLE_DIFFUSION_EXTRA_SINGLE_IMAGE_PATH, json_data=request_data - ) - result = decode_base64_image(response["image"]) - - ih, iw, _ = get_h_w_c(image) - rh, rw, _ = get_h_w_c(result) - ratio_w = width / iw - ratio_h = height / ih - if ratio_w > ratio_h: - larger_ratio = ratio_w - else: - larger_ratio = ratio_h - - if mode == UpscalerMode.SCALE_TO: - if crop: - assert ( - (rw, rh) == (width, height) - ), f"Expected the returned image to be {width}x{height}px but found {rw}x{rh}px instead " - else: - assert ( - (rw, rh) == (int(iw * larger_ratio), int(ih * larger_ratio)) - ), f"Expected the returned image to be {width}x{height}px but found {rw}x{rh}px instead " - else: - assert ( - (rw, rh) == (int(iw * upscaling_resize), int(ih * upscaling_resize)) - ), f"Expected the returned image to be {width}x{height}px but found {rw}x{rh}px instead " - - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/features.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/features.py deleted file mode 100644 index da989f7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/features.py +++ /dev/null @@ -1,32 +0,0 @@ -from api import FeatureState -from nodes.utils.format import join_english - -from . import web_ui_feature_descriptor -from .web_ui import ApiConfig, get_verified_api - - -async def check_connection() -> FeatureState: - config = None - try: - config = ApiConfig.from_env() - api = await get_verified_api() - if api is not None: - return FeatureState.enabled(f"Connected to {api.base_url}") - else: - url = config.host - if len(config.protocol) == 1: - url = f"{config.protocol[0]}://{url}" - if len(config.port) == 1: - url += f":{config.port[0]}" - else: - ports = join_english(config.port) - url += f" on ports {ports}" - - return FeatureState.disabled( - f"No stable diffusion API found. Could not connect to {url}." - ) - except Exception as e: - return FeatureState.disabled(f"Could not connect to stable diffusion API: {e}") - - -web_ui = web_ui_feature_descriptor.add_behavior(check=check_connection) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/util.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/util.py deleted file mode 100644 index 2b33265..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/util.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -import base64 -import io - -import cv2 -import numpy as np -from PIL import Image - -from nodes.impl.image_utils import normalize, to_uint8 -from nodes.utils.utils import get_h_w_c - - -def nearest_valid_size(width: int, height: int): - return (width // 8) * 8, (height // 8) * 8 - - -def decode_base64_image(image_bytes: bytes | str) -> np.ndarray: - image = Image.open(io.BytesIO(base64.b64decode(image_bytes))) - image_nparray = np.array(image) - _, _, c = get_h_w_c(image_nparray) - if c == 3: - image_nparray = cv2.cvtColor(image_nparray, cv2.COLOR_RGB2BGR) - elif c == 4: - image_nparray = cv2.cvtColor(image_nparray, cv2.COLOR_RGBA2BGRA) - return normalize(image_nparray) - - -def encode_base64_image(image_nparray: np.ndarray) -> str: - image_nparray = to_uint8(image_nparray) - _, _, c = get_h_w_c(image_nparray) - if c == 1: - # PIL supports grayscale images just fine, so we don't need to do any conversion - pass - elif c == 3: - image_nparray = cv2.cvtColor(image_nparray, cv2.COLOR_BGR2RGB) - elif c == 4: - image_nparray = cv2.cvtColor(image_nparray, cv2.COLOR_BGRA2RGBA) - else: - raise RuntimeError - with io.BytesIO() as buffer: - with Image.fromarray(image_nparray) as image: - image.save(buffer, format="PNG") - return base64.b64encode(buffer.getvalue()).decode("utf-8") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/web_ui.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/web_ui.py deleted file mode 100644 index 47363ec..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_external/web_ui.py +++ /dev/null @@ -1,263 +0,0 @@ -from __future__ import annotations - -import asyncio -import os -from concurrent.futures import ThreadPoolExecutor -from dataclasses import dataclass -from enum import Enum - -import requests -from sanic.log import logger - -STABLE_DIFFUSION_TEXT2IMG_PATH = "/sdapi/v1/txt2img" -STABLE_DIFFUSION_IMG2IMG_PATH = "/sdapi/v1/img2img" -STABLE_DIFFUSION_INTERROGATE_PATH = "/sdapi/v1/interrogate" -STABLE_DIFFUSION_OPTIONS_PATH = "/sdapi/v1/options" -STABLE_DIFFUSION_EXTRA_SINGLE_IMAGE_PATH = "/sdapi/v1/extra-single-image" -STABLE_DIFFUSION_UPSCALERS_PATH = "/sdapi/v1/upscalers" - -TIMEOUT_MSG = """Stable diffusion request timeout reached.""" - -STABLE_DIFFUSION_REQUEST_TIMEOUT = float( - os.environ.get("STABLE_DIFFUSION_REQUEST_TIMEOUT", None) or "600" -) # 10 minutes - -_thread_pool = ThreadPoolExecutor(max_workers=4) - - -@dataclass -class Api: - protocol: str - host: str - port: str - - @property - def base_url(self) -> str: - return f"{self.protocol}://{self.host}:{self.port}" - - def get_url(self, path: str) -> str: - return f"{self.base_url}{path}" - - def get(self, path: str, timeout: float = STABLE_DIFFUSION_REQUEST_TIMEOUT) -> dict: - try: - response = requests.get(self.get_url(path), timeout=timeout) - if response.status_code != 200: - raise ExternalServiceHTTPError( - f"webui GET request to {path} returned status code: {response.status_code}: {response.text}" - ) - return response.json() - except requests.ConnectionError as exc: - raise ExternalServiceConnectionError( - f"webui GET request to {path} connection failed" - ) from exc - except requests.exceptions.ReadTimeout as exc: - raise ExternalServiceTimeoutError(TIMEOUT_MSG) from exc - - async def get_async( - self, path: str, timeout: float = STABLE_DIFFUSION_REQUEST_TIMEOUT - ) -> dict: - def run(): - return self.get(path, timeout) - - loop = asyncio.get_event_loop() - result = await loop.run_in_executor(_thread_pool, run) - return result - - def post(self, path: str, json_data: dict) -> dict: - try: - response = requests.post( - self.get_url(path), - json=json_data, - timeout=STABLE_DIFFUSION_REQUEST_TIMEOUT, - ) - if response.status_code != 200: - raise ExternalServiceHTTPError( - f"webui POST request to {path} returned status code: {response.status_code}: {response.text}" - ) - return response.json() - except requests.ConnectionError as exc: - raise ExternalServiceConnectionError( - f"webui POST request to {path} connection failed" - ) from exc - except requests.exceptions.ReadTimeout as exc: - raise ExternalServiceTimeoutError(TIMEOUT_MSG) from exc - - -@dataclass -class ApiConfig: - protocol: list[str] - host: str - port: list[str] - - @staticmethod - def from_env(): - protocol = os.environ.get("STABLE_DIFFUSION_PROTOCOL", None) - host = os.environ.get("STABLE_DIFFUSION_HOST", "127.0.0.1") - port = os.environ.get("STABLE_DIFFUSION_PORT", None) - - if protocol: - protocol = [protocol] - else: - protocol = ["https", "http"] - - if port: - port = [port] - else: - port = ["7860", "7861"] - - return ApiConfig(protocol, host, port) - - def list_apis(self) -> list[Api]: - apis: list[Api] = [] - for protocol in self.protocol: - for port in self.port: - apis.append(Api(protocol, self.host, port)) - return apis - - -_CURRENT_API: Api | None = None - - -async def get_verified_api() -> Api | None: - timeout = 1 # seconds - - global _CURRENT_API # pylint: disable=global-statement - if _CURRENT_API is not None: - # redo check to see if it's still alive - try: - await _CURRENT_API.get_async(STABLE_DIFFUSION_OPTIONS_PATH, timeout=timeout) - return _CURRENT_API - except Exception: - _CURRENT_API = None - - # check all apis in parallel - apis = ApiConfig.from_env().list_apis() - assert len(apis) > 0 - tasks = [ - api.get_async(STABLE_DIFFUSION_OPTIONS_PATH, timeout=timeout) for api in apis - ] - # because good API design just isn't pythonic, asyncio.gather will return List[Any]. - results = await asyncio.gather(*tasks, return_exceptions=True) - - # find the first working api - for api, result in zip(apis, results): - if not isinstance(result, Exception): - logger.info(f"Found stable diffusion API at {api.base_url}") - _CURRENT_API = api - return api - - return None - - -def get_api() -> Api: - if _CURRENT_API is not None: - return _CURRENT_API - - api = asyncio.run(get_verified_api()) - if api is None: - raise RuntimeError("No stable diffusion API found") - return api - - -class ExternalServiceHTTPError(Exception): - pass - - -class ExternalServiceConnectionError(Exception): - pass - - -class ExternalServiceTimeoutError(Exception): - pass - - -class UpscalerName(Enum): - LANCZOS = "Lanczos" - NEAREST = "Nearest" - LDSR = "LDSR" - ESRGAN_4 = "ESRGAN_4x" - REAL_ESRGAN_4 = "R-ESRGAN 4x+" - REAL_ESRGAN_4_ANIME6B = "R-ESRGAN 4x+ Anime6B" - SCUNET_GAN = "ScuNET GAN" - SCUNET_PSNR = "ScuNET PSNR" - SWIN_IR_4 = "SwinIR 4x" - - -UPSCALE_NAME_LABELS = { - UpscalerName.LANCZOS: "Lanczos", - UpscalerName.NEAREST: "Nearest", - UpscalerName.LDSR: "LDSR", - UpscalerName.ESRGAN_4: "ESRGAN 4x", - UpscalerName.REAL_ESRGAN_4: "Real ESRGAN 4x+", - UpscalerName.REAL_ESRGAN_4_ANIME6B: "Real ESRGAN 4x+ Anime6B", - UpscalerName.SCUNET_GAN: "ScuNET GAN", - UpscalerName.SCUNET_PSNR: "ScuNET PSNR", - UpscalerName.SWIN_IR_4: "SwinIR 4x", -} - - -class SamplerName(Enum): - EULER = "Euler" - EULER_A = "Euler a" - LMS = "LMS" - HEUN = "Heun" - DPM2 = "DPM2" - DPM2_A = "DPM2 a" - DPMpp_2S_A = "DPM++ 2S a" - DPMpp_2M = "DPM++ 2M" - DPMpp_SDE = "DPM++ SDE" - DPM_FAST = "DPM fast" - DPM_A = "DPM adaptive" - LMS_KARRAS = "LMS Karras" - DPM2_KARRAS = "DPM2 Karras" - DPM2_A_KARRAS = "DPM2 a Karras" - DPMpp_2S_A_KARRAS = "DPM++ 2S a Karras" - DPMpp_2M_KARRAS = "DPM++ 2M Karras" - DPMpp_SDE_KARRAS = "DPM++ SDE Karras" - DDIM = "DDIM" - PLMS = "PLMS" - - -SAMPLER_NAME_LABELS = { - SamplerName.EULER: "Euler", - SamplerName.EULER_A: "Euler a", - SamplerName.LMS: "LMS", - SamplerName.HEUN: "Heun", - SamplerName.DPM2: "DPM2", - SamplerName.DPM2_A: "DPM2 a", - SamplerName.DPMpp_2S_A: "DPM++ 2S a", - SamplerName.DPMpp_2M: "DPM++ 2M", - SamplerName.DPMpp_SDE: "DPM++ SDE", - SamplerName.DPM_FAST: "DPM fast", - SamplerName.DPM_A: "DPM adaptive", - SamplerName.LMS_KARRAS: "LMS Karras", - SamplerName.DPM2_KARRAS: "DPM2 Karras", - SamplerName.DPM2_A_KARRAS: "DPM2 a Karras", - SamplerName.DPMpp_2S_A_KARRAS: "DPM++ 2S a Karras", - SamplerName.DPMpp_2M_KARRAS: "DPM++ 2M Karras", - SamplerName.DPMpp_SDE_KARRAS: "DPM++ SDE Karras", - SamplerName.DDIM: "DDIM", - SamplerName.PLMS: "PLMS", -} - - -class ResizeMode(Enum): - JUST_RESIZE = 0 - CROP_AND_RESIZE = 1 - RESIZE_AND_FILL = 2 - LATENT_UPSCALE = 3 - - -RESIZE_MODE_LABELS = { - ResizeMode.JUST_RESIZE: "Just resize", - ResizeMode.CROP_AND_RESIZE: "Crop and resize", - ResizeMode.RESIZE_AND_FILL: "Resize and fill", - ResizeMode.LATENT_UPSCALE: "Just resize (Latent upscale)", -} - - -class InpaintingFill(Enum): - FILL = 0 - ORIGINAL = 1 - LATENT_NOISE = 2 - LATENT_NOTHING = 3 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/__init__.py deleted file mode 100644 index 959d821..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/__init__.py +++ /dev/null @@ -1,50 +0,0 @@ -from sanic.log import logger - -from api import MB, Dependency, add_package -from system import is_arm_mac, is_mac - -general = "NCNN uses .bin/.param models to upscale images." -recommendation = "It is recommended for AMD users" - -if is_arm_mac: - inst_hint = general -elif is_mac: - inst_hint = f"{general} {recommendation}." -else: - inst_hint = ( - f"{general} {recommendation} because it supports both AMD and Nvidia GPUs." - ) - - -package = add_package( - __file__, - id="chaiNNer_ncnn", - name="NCNN", - description=( - f"{general} Models can be converted from PyTorch to NCNN, which requires ONNX" - " to be installed as well.\n\nNCNN utilizes Vulkan for GPU acceleration," - " meaning it supports any modern GPU. However, in some cases GPU upscaling" - " may fail, due to NCNN on Vulkan being experimental." - ), - dependencies=[ - Dependency( - display_name="NCNN", - pypi_name="ncnn-vulkan", - version="2023.6.18", - size_estimate=7 * MB if is_mac else 4 * MB, - import_name="ncnn_vulkan", - ), - ], - icon="NCNN", - color="#ED64A6", -) - -ncnn_category = package.add_category( - name="NCNN", - description="Nodes for using the NCNN Neural Network Framework with images.", - icon="NCNN", - color="#ED64A6", - install_hint=inst_hint, -) - -logger.debug(f"Loaded package {package.name}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/__init__.py deleted file mode 100644 index 3325bba..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .. import ncnn_category - -io_group = ncnn_category.add_node_group("Input & Output") -processing_group = ncnn_category.add_node_group("Processing") -utility_group = ncnn_category.add_node_group("Utility") -batch_processing_group = ncnn_category.add_node_group("Batch Processing") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/batch_processing/load_models.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/batch_processing/load_models.py deleted file mode 100644 index 0443df3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/batch_processing/load_models.py +++ /dev/null @@ -1,85 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -from sanic.log import logger - -from api import Iterator, IteratorOutputInfo -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.properties.inputs import BoolInput, DirectoryInput -from nodes.properties.outputs import ( - DirectoryOutput, - NcnnModelOutput, - NumberOutput, - TextOutput, -) -from nodes.utils.utils import list_all_files_sorted - -from .. import batch_processing_group -from ..io.load_model import load_model_node - - -@batch_processing_group.register( - schema_id="chainner:ncnn:load_models", - name="Load Models", - description=( - "Iterate over all files in a directory and run the provided nodes on just the" - " NCNN model files (.param/.bin). Supports the same models as" - " `chainner:ncnn:load_model`." - ), - icon="MdLoop", - inputs=[ - DirectoryInput(), - BoolInput("Stop on first error", default=False).with_docs( - "Instead of collecting errors and throwing them at the end of processing, stop iteration and throw an error as soon as one occurs.", - hint=True, - ), - ], - outputs=[ - NcnnModelOutput(), - DirectoryOutput("Directory", output_type="Input0"), - TextOutput("Subdirectory Path"), - TextOutput("Name"), - NumberOutput("Index", output_type="uint").with_docs( - "A counter that starts at 0 and increments by 1 for each model." - ), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 2, 3, 4]), - kind="newIterator", -) -def load_models_node( - directory: Path, - fail_fast: bool, -) -> tuple[Iterator[tuple[NcnnModelWrapper, str, str, int]], Path]: - logger.debug(f"Iterating over models in directory: {directory}") - - def load_model(filepath_pairs: tuple[Path, Path], index: int): - model, dirname, basename = load_model_node(filepath_pairs[0], filepath_pairs[1]) - # Get relative path from root directory passed by Iterator directory input - rel_path = os.path.relpath(dirname, directory) - return model, rel_path, basename, index - - param_files: list[Path] = list_all_files_sorted(directory, [".param"]) - bin_files: list[Path] = list_all_files_sorted(directory, [".bin"]) - - if len(param_files) != len(bin_files): - raise ValueError( - "The number of param files and bin files are not the same. Please check" - " your directory." - ) - - # Check if the filenames match - for param_file, bin_file in zip(param_files, bin_files): - param_file_name, _ = os.path.splitext(param_file) - bin_file_name, _ = os.path.splitext(bin_file) - - if param_file_name != bin_file_name: - raise ValueError( - f"Param file {param_file_name} does not match bin file {bin_file_name}." - " Please check your files." - ) - - model_files = list(zip(param_files, bin_files)) - - return Iterator.from_list(model_files, load_model, fail_fast), directory diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/io/load_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/io/load_model.py deleted file mode 100644 index 9f35970..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/io/load_model.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from nodes.groups import ncnn_file_inputs_group -from nodes.impl.ncnn.model import NcnnModel, NcnnModelWrapper -from nodes.impl.ncnn.optimizer import NcnnOptimizer -from nodes.properties.inputs import BinFileInput, ParamFileInput -from nodes.properties.outputs import DirectoryOutput, FileNameOutput, NcnnModelOutput -from nodes.utils.utils import split_file_path - -from .. import io_group - - -@io_group.register( - schema_id="chainner:ncnn:load_model", - name="Load Model", - description=( - "Load NCNN model (.bin and .param files). Theoretically supports any NCNN" - " Super-Resolution model that doesn't expect non-standard preprocessing." - ), - icon="NCNN", - inputs=[ - ncnn_file_inputs_group( - ParamFileInput(primary_input=True), - BinFileInput(primary_input=True), - ) - ], - outputs=[ - NcnnModelOutput(kind="tagged").suggest(), - DirectoryOutput("Directory", of_input=0).with_id(2), - FileNameOutput("Name", of_input=0).with_id(1), - ], - see_also=[ - "chainner:ncnn:load_models", - ], -) -def load_model_node( - param_path: Path, bin_path: Path -) -> tuple[NcnnModelWrapper, Path, str]: - model = NcnnModel.load_from_file(str(param_path), str(bin_path)) - NcnnOptimizer(model).optimize() - - model_dir, model_name, _ = split_file_path(param_path) - - return NcnnModelWrapper(model), model_dir, model_name diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/io/save_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/io/save_model.py deleted file mode 100644 index e14de10..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/io/save_model.py +++ /dev/null @@ -1,32 +0,0 @@ -from pathlib import Path - -from sanic.log import logger - -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.properties.inputs import DirectoryInput, NcnnModelInput, RelativePathInput - -from .. import io_group - - -@io_group.register( - schema_id="chainner:ncnn:save_model", - name="Save Model", - description="Save an NCNN model to specified directory. It can also be saved in fp16 mode for smaller file size and faster processing.", - icon="MdSave", - inputs=[ - NcnnModelInput(), - DirectoryInput(must_exist=False), - RelativePathInput("Param/Bin Name"), - ], - outputs=[], - side_effects=True, -) -def save_model_node(model: NcnnModelWrapper, directory: Path, name: str) -> None: - full_bin_path = (directory / f"{name}.bin").resolve() - full_param_path = (directory / f"{name}.param").resolve() - - logger.debug(f"Writing NCNN model to paths: {full_bin_path} {full_param_path}") - - full_bin_path.parent.mkdir(parents=True, exist_ok=True) - model.model.write_bin(full_bin_path) - model.model.write_param(full_param_path) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/processing/upscale_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/processing/upscale_image.py deleted file mode 100644 index 795339e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/processing/upscale_image.py +++ /dev/null @@ -1,234 +0,0 @@ -from __future__ import annotations - -from contextlib import contextmanager - -import cv2 -import numpy as np - -try: - from ncnn_vulkan import ncnn - - use_gpu = True -except ImportError: - from ncnn import ncnn # type: ignore - - use_gpu = False -from sanic.log import logger - -from api import NodeContext -from nodes.groups import Condition, if_enum_group, if_group -from nodes.impl.ncnn.auto_split import ncnn_auto_split -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.impl.ncnn.session import get_ncnn_net -from nodes.impl.upscale.auto_split_tiles import ( - CUSTOM, - TILE_SIZE_256, - TileSize, - estimate_tile_size, - parse_tile_size_input, -) -from nodes.impl.upscale.convenient_upscale import convenient_upscale -from nodes.impl.upscale.tiler import MaxTileSize -from nodes.properties.inputs import ( - BoolInput, - ImageInput, - NcnnModelInput, - NumberInput, - TileSizeDropdown, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c -from system import is_mac - -from ...settings import NcnnSettings, get_settings -from .. import processing_group - - -@contextmanager -def managed_blob_vkallocator(vkdev: ncnn.VulkanDevice): - try: - blob_vkallocator = vkdev.acquire_blob_allocator() - except Exception: - blob_vkallocator = ncnn.VkBlobAllocator(vkdev) - try: - yield blob_vkallocator - finally: - blob_vkallocator.clear() - - -@contextmanager -def ncnn_allocators(vkdev: ncnn.VulkanDevice): - with managed_blob_vkallocator(vkdev) as blob_vkallocator: - try: - staging_vkallocator = vkdev.acquire_staging_allocator() - except Exception: - staging_vkallocator = ncnn.VkStagingAllocator(vkdev) - try: - yield blob_vkallocator, staging_vkallocator - finally: - staging_vkallocator.clear() - - -def upscale_impl( - settings: NcnnSettings, - img: np.ndarray, - model: NcnnModelWrapper, - input_name: str, - output_name: str, - tile_size: TileSize, -): - net = get_ncnn_net(model, settings=settings) - # Try/except block to catch errors - try: - - def estimate(): - heap_budget_bytes = settings.budget_limit * 1024**3 - - # 0 means no limit, we approximate that here by picking 1 PiB, - # which presumably no one will ever hit. - if heap_budget_bytes == 0: - heap_budget_bytes = 1024**5 - - model_size_estimate = model.model.bin_length - - if use_gpu: - if is_mac: - # the actual estimate frequently crashes on mac, so we just use 256 - return MaxTileSize(256) - - heap_budget_bytes = min( - heap_budget_bytes, vkdev.get_heap_budget() * 1024 * 1024 * 0.8 - ) - else: - # Empirically determined (TODO do a more thorough job here) - model_size_estimate = model_size_estimate * 5 / 3 - if net.opt.use_winograd_convolution: - model_size_estimate = model_size_estimate * 11 / 5 - elif net.opt.use_sgemm_convolution: - model_size_estimate = model_size_estimate * 40 / 5 - - return MaxTileSize( - estimate_tile_size(heap_budget_bytes, int(model_size_estimate), img, 4) - ) - - if use_gpu: - vkdev = ncnn.get_gpu_device(settings.gpu_index) - - with ncnn_allocators(vkdev) as ( - blob_vkallocator, - staging_vkallocator, - ): - return ncnn_auto_split( - img, - net, - input_name=input_name, - output_name=output_name, - blob_vkallocator=blob_vkallocator, - staging_vkallocator=staging_vkallocator, - tiler=parse_tile_size_input(tile_size, estimate), - ) - else: - return ncnn_auto_split( - img, - net, - input_name=input_name, - output_name=output_name, - blob_vkallocator=None, - staging_vkallocator=None, - tiler=parse_tile_size_input(tile_size, estimate), - ) - except (RuntimeError, ValueError): - raise - except Exception as e: - logger.error(e) - raise RuntimeError("An unexpected error occurred during NCNN processing.") # noqa: B904 - - -@processing_group.register( - schema_id="chainner:ncnn:upscale_image", - name="Upscale Image", - description="Upscale an image with NCNN. Unlike PyTorch, NCNN has GPU support on all devices, assuming your drivers support Vulkan. \ - Select a manual number of tiles or set a memory budget limit if you are having issues with the automatic mode.", - icon="NCNN", - inputs=[ - ImageInput().with_id(1), - NcnnModelInput().with_id(0), - TileSizeDropdown() - .with_id(2) - .with_docs( - "Tiled upscaling is used to allow large images to be upscaled without hitting memory limits.", - "This works by splitting the image into tiles (with overlap), upscaling each tile individually, and seamlessly recombining them.", - "Generally it's recommended to use the largest tile size possible for best performance (with the ideal scenario being no tiling at all), but depending on the model and image size, this may not be possible.", - "If you are having issues with the automatic mode, you can manually select a tile size, or set a memory budget limit. On certain machines, a very small tile size such as 256 or 128 might be required for it to work at all.", - ), - if_enum_group(2, CUSTOM)( - NumberInput( - "Custom Tile Size", - minimum=1, - maximum=None, - default=TILE_SIZE_256, - precision=0, - controls_step=1, - unit="px", - has_handle=False, - ) - ), - if_group( - Condition.type(1, "Image { channels: 4 } ") - & ( - Condition.type(0, "NcnnNetwork { inputChannels: 1, outputChannels: 1 }") - | Condition.type( - 0, "NcnnNetwork { inputChannels: 3, outputChannels: 3 }" - ) - ) - )( - BoolInput("Separate Alpha", default=False).with_docs( - "Upscale alpha separately from color. Enabling this option will cause the alpha of" - " the upscaled image to be less noisy and more accurate to the alpha of the original" - " image, but the image may suffer from dark borders near transparency edges" - " (transition from fully transparent to fully opaque).", - "Whether enabling this option will improve the upscaled image depends on the original" - " image. We generally recommend this option for images with smooth transitions between" - " transparent and opaque regions.", - ) - ), - ], - outputs=[ - ImageOutput(image_type="""convenientUpscale(Input0, Input1)"""), - ], - limited_to_8bpc=True, - node_context=True, -) -def upscale_image_node( - context: NodeContext, - img: np.ndarray, - model: NcnnModelWrapper, - tile_size: TileSize, - custom_tile_size: int | None, - separate_alpha: bool, -) -> np.ndarray: - settings = get_settings(context) - - def upscale(i: np.ndarray) -> np.ndarray: - ic = get_h_w_c(i)[2] - if ic == 3: - i = cv2.cvtColor(i, cv2.COLOR_BGR2RGB) - elif ic == 4: - i = cv2.cvtColor(i, cv2.COLOR_BGRA2RGBA) - i = upscale_impl( - settings, - i, - model, - model.model.layers[0].outputs[0], - model.model.layers[-1].outputs[0], - TileSize(custom_tile_size) - if tile_size == CUSTOM and custom_tile_size is not None - else tile_size, - ) - if ic == 3: - i = cv2.cvtColor(i, cv2.COLOR_RGB2BGR) - elif ic == 4: - i = cv2.cvtColor(i, cv2.COLOR_RGBA2BGRA) - return i - - return convenient_upscale(img, model.in_nc, model.out_nc, upscale, separate_alpha) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/utility/get_model_scale.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/utility/get_model_scale.py deleted file mode 100644 index 5c04c29..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/utility/get_model_scale.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import annotations - -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.properties.inputs import NcnnModelInput -from nodes.properties.outputs import NumberOutput - -from .. import utility_group - - -@utility_group.register( - schema_id="chainner:ncnn:model_dim", - name="Get Model Scale", - description="""Returns the scale of an NCNN model.""", - icon="BsRulers", - inputs=[NcnnModelInput()], - outputs=[NumberOutput("Scale", output_type="Input0.scale")], -) -def get_model_scale_node(model: NcnnModelWrapper) -> int: - return model.scale diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/utility/interpolate_models.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/utility/interpolate_models.py deleted file mode 100644 index 3d54650..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/ncnn/utility/interpolate_models.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from api import NodeContext -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.impl.upscale.auto_split_tiles import NO_TILING -from nodes.properties.inputs import NcnnModelInput, SliderInput -from nodes.properties.outputs import NcnnModelOutput, NumberOutput - -from .. import utility_group -from ..processing.upscale_image import upscale_image_node - - -def check_will_upscale(context: NodeContext, interp: NcnnModelWrapper): - fake_img = np.ones((3, 3, 3), dtype=np.float32, order="F") - result = upscale_image_node(context, fake_img, interp, NO_TILING, None, False) - - mean_color = np.mean(result) - del result - return mean_color > 0.5 - - -@utility_group.register( - schema_id="chainner:ncnn:interpolate_models", - name="Interpolate Models", - description="""Interpolate two NCNN models of the same type together. Note: models must share a common 'pretrained model' ancestor - in order to be interpolatable.""", - icon="BsTornado", - inputs=[ - NcnnModelInput("Model A"), - NcnnModelInput("Model B"), - SliderInput( - "Weights", - controls_step=5, - slider_step=1, - maximum=100, - default=50, - unit="%", - note_expression="`Model A ${100 - value}% ― Model B ${value}%`", - ends=("A", "B"), - ), - ], - outputs=[ - NcnnModelOutput(), - NumberOutput("Amount A", output_type="100 - Input2"), - NumberOutput("Amount B", output_type="Input2"), - ], - node_context=True, -) -def interpolate_models_node( - context: NodeContext, - model_a: NcnnModelWrapper, - model_b: NcnnModelWrapper, - amount: int, -) -> tuple[NcnnModelWrapper, int, int]: - if amount == 0: - return model_a, 100, 0 - elif amount == 100: - return model_b, 0, 100 - - f_amount = 1 - amount / 100 - interp_model = NcnnModelWrapper(model_a.model.interpolate(model_b.model, f_amount)) - - if not check_will_upscale(context, interp_model): - raise ValueError( - "These NCNN models are not compatible and not able to be interpolated together" - ) - - return interp_model, 100 - amount, amount diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/settings.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/settings.py deleted file mode 100644 index ee6f1c3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_ncnn/settings.py +++ /dev/null @@ -1,118 +0,0 @@ -from dataclasses import dataclass - -try: - from ncnn_vulkan import ncnn - - use_gpu = True -except ImportError: - from ncnn import ncnn # type: ignore - - use_gpu = False - -from api import DropdownSetting, NodeContext, NumberSetting, ToggleSetting -from system import is_arm_mac - -from . import package - -if not is_arm_mac and use_gpu: - try: - gpu_list = [] - for i in range(ncnn.get_gpu_count()): - gpu_list.append(ncnn.get_gpu_info(i).device_name()) - - package.add_setting( - DropdownSetting( - label="GPU", - key="gpu_index", - description="Which GPU to use for NCNN. This is only relevant if you have multiple GPUs.", - options=[{"label": x, "value": str(i)} for i, x in enumerate(gpu_list)], - default="0", - ) - ) - except Exception: - pass - -default_net_opt = ncnn.Net().opt - -# Haven't tested disabling Winograd/SGEMM in the ncnn_vulkan fork, so only -# allow it with upstream ncnn for now. It should work fine regardless of -# CPU/GPU, but I only tested with CPU. Ditto for multithreading, except it -# only makes sense for CPU. -if not use_gpu: - package.add_setting( - ToggleSetting( - label="Use Winograd", - key="winograd", - description="Enable Winograd convolution for NCNN. Typically faster but uses more memory.", - default=default_net_opt.use_winograd_convolution, - ) - ) - package.add_setting( - ToggleSetting( - label="Use SGEMM", - key="sgemm", - description="Enable SGEMM convolution for NCNN. Typically faster but uses more memory.", - default=default_net_opt.use_sgemm_convolution, - ) - ) - - package.add_setting( - NumberSetting( - label="Thread Count", - key="threads", - description="Number of threads for NCNN. Only affects CPU mode.", - default=default_net_opt.num_threads, - min=1, - max=default_net_opt.num_threads, - ) - ) - package.add_setting( - NumberSetting( - label="Block Time", - key="blocktime", - description="Milliseconds for threads to busy-wait for more work before going to sleep. Higher values may be faster; lower values may decrease power consumption and CPU usage. Only affects CPU mode.", - default=default_net_opt.openmp_blocktime, - min=0, - max=400, - ) - ) - -package.add_setting( - NumberSetting( - label="Memory Budget Limit (GiB)", - key="budget_limit", - description="Maximum memory to use for NCNN inference. 0 means no limit. Memory usage measurement is not completely accurate yet; you may need to significantly adjust this budget limit via trial-and-error if it's not having the effect you want.", - default=0, - min=0, - max=1024**2, - ) -) - - -@dataclass(frozen=True) -class NcnnSettings: - gpu_index: int - winograd: bool - sgemm: bool - threads: int - blocktime: int - budget_limit: int - - -def get_settings(context: NodeContext) -> NcnnSettings: - settings = context.settings - - return NcnnSettings( - gpu_index=settings.get_int("gpu_index", 0, parse_str=True), - winograd=settings.get_bool( - "winograd", default_net_opt.use_winograd_convolution - ), - sgemm=settings.get_bool("sgemm", default_net_opt.use_sgemm_convolution), - threads=settings.get_int( - "threads", default_net_opt.num_threads, parse_str=True - ), - blocktime=settings.get_int( - "blocktime", default_net_opt.openmp_blocktime, parse_str=True - ), - budget_limit=settings.get_int("budget_limit", 0, parse_str=True), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/__init__.py deleted file mode 100644 index e99d174..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -from sanic.log import logger - -from api import KB, MB, Dependency, add_package -from gpu import nvidia -from system import is_arm_mac - -general = "ONNX uses .onnx models to upscale images." -conversion = "It also helps to convert between PyTorch and NCNN." - -if is_arm_mac: - package_description = f"{general} {conversion} However, it does not support CoreML." - inst_hint = general -else: - package_description = ( - f"{general} {conversion} It is fastest when CUDA is supported. If TensorRT is" - " installed on the system, it can also be configured to use that." - ) - inst_hint = f"{general} It does not support AMD GPUs." - - -def get_onnx_runtime(): - if nvidia.is_available: - return Dependency( - display_name="ONNX Runtime (GPU)", - pypi_name="onnxruntime-gpu", - version="1.17.1", - size_estimate=120 * MB, - import_name="onnxruntime", - extra_index_url="https://aiinfra.pkgs.visualstudio.com/PublicPackages/_packaging/onnxruntime-cuda-12/pypi/simple/", - ) - else: - return Dependency( - display_name="ONNX Runtime", - pypi_name="onnxruntime", - version="1.17.1", - size_estimate=6 * MB, - ) - - -package = add_package( - __file__, - id="chaiNNer_onnx", - name="ONNX", - description=package_description, - dependencies=[ - Dependency( - display_name="ONNX", - pypi_name="onnx", - version="1.16.0", - size_estimate=12 * MB, - ), - Dependency( - display_name="ONNX Optimizer", - pypi_name="onnxoptimizer", - version="0.3.13", - size_estimate=300 * KB, - ), - get_onnx_runtime(), - Dependency( - display_name="Protobuf", - pypi_name="protobuf", - version="4.24.2", - size_estimate=500 * KB, - ), - ], - icon="ONNX", - color="#63B3ED", -) - - -onnx_category = package.add_category( - name="ONNX", - description="Nodes for using the ONNX Neural Network Framework with images.", - icon="ONNX", - color="#63B3ED", - install_hint=inst_hint, -) - - -logger.debug(f"Loaded package {package.name}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/__init__.py deleted file mode 100644 index c862db9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .. import onnx_category - -io_group = onnx_category.add_node_group("Input & Output") -processing_group = onnx_category.add_node_group("Processing") -utility_group = onnx_category.add_node_group("Utility") -batch_processing_group = onnx_category.add_node_group("Batch Processing") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/batch_processing/load_models.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/batch_processing/load_models.py deleted file mode 100644 index 3a85ae1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/batch_processing/load_models.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -from sanic.log import logger - -from api import Iterator, IteratorOutputInfo -from nodes.impl.onnx.model import OnnxModel -from nodes.properties.inputs import BoolInput, DirectoryInput -from nodes.properties.outputs import ( - DirectoryOutput, - NumberOutput, - OnnxModelOutput, - TextOutput, -) -from nodes.utils.utils import list_all_files_sorted - -from .. import batch_processing_group -from ..io.load_model import load_model_node - - -@batch_processing_group.register( - schema_id="chainner:onnx:load_models", - name="Load Models", - description=( - "Iterate over all files in a directory and run the provided nodes on just the" - " ONNX model files (.onnx). Supports the same models as" - " `chainner:onnx:load_model`." - ), - icon="MdLoop", - inputs=[ - DirectoryInput(), - BoolInput("Stop on first error", default=False).with_docs( - "Instead of collecting errors and throwing them at the end of processing, stop iteration and throw an error as soon as one occurs.", - hint=True, - ), - ], - outputs=[ - OnnxModelOutput(), - DirectoryOutput("Directory", output_type="Input0"), - TextOutput("Subdirectory Path"), - TextOutput("Name"), - NumberOutput("Index", output_type="uint").with_docs( - "A counter that starts at 0 and increments by 1 for each model." - ), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 2, 3, 4]), - kind="newIterator", -) -def load_models_node( - directory: Path, - fail_fast: bool, -) -> tuple[Iterator[tuple[OnnxModel, str, str, int]], Path]: - logger.debug(f"Iterating over models in directory: {directory}") - - def load_model(path: Path, index: int): - model, dirname, basename = load_model_node(path) - # Get relative path from root directory passed by Iterator directory input - rel_path = os.path.relpath(dirname, directory) - return model, rel_path, basename, index - - supported_filetypes = [".onnx"] - model_files = list_all_files_sorted(directory, supported_filetypes) - - return Iterator.from_list(model_files, load_model, fail_fast), directory diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/io/load_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/io/load_model.py deleted file mode 100644 index 3923c69..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/io/load_model.py +++ /dev/null @@ -1,47 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -import onnx -from sanic.log import logger - -from nodes.impl.onnx.model import OnnxModel, load_onnx_model -from nodes.properties.inputs import OnnxFileInput -from nodes.properties.outputs import DirectoryOutput, FileNameOutput, OnnxModelOutput -from nodes.utils.utils import split_file_path - -from .. import io_group - - -@io_group.register( - schema_id="chainner:onnx:load_model", - name="Load Model", - description=( - "Load ONNX model file (.onnx). Theoretically supports any ONNX Super-Resolution" - " model that doesn't expect non-standard preprocessing. Also supports RemBG" - " background removal models." - ), - icon="ONNX", - inputs=[OnnxFileInput(primary_input=True)], - outputs=[ - OnnxModelOutput().suggest(), - DirectoryOutput("Directory", of_input=0).with_id(2), - FileNameOutput("Name", of_input=0).with_id(1), - ], - see_also=[ - "chainner:onnx:load_models", - ], -) -def load_model_node(path: Path) -> tuple[OnnxModel, Path, str]: - assert os.path.exists(path), f"Model file at location {path} does not exist" - - assert os.path.isfile(path), f"Path {path} is not a file" - - logger.debug(f"Reading onnx model from path: {path}") - model = onnx.load_model(str(path)) - - model_as_string = model.SerializeToString() - - dirname, basename, _ = split_file_path(path) - return load_onnx_model(model_as_string), dirname, basename diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/io/save_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/io/save_model.py deleted file mode 100644 index 197ae48..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/io/save_model.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from sanic.log import logger - -from nodes.impl.onnx.model import OnnxModel -from nodes.properties.inputs import DirectoryInput, OnnxModelInput, RelativePathInput - -from .. import io_group - - -@io_group.register( - schema_id="chainner:onnx:save_model", - name="Save Model", - description="""Save ONNX model to file (.onnx).""", - icon="MdSave", - inputs=[ - OnnxModelInput(), - DirectoryInput(must_exist=False), - RelativePathInput("Model Name"), - ], - outputs=[], - side_effects=True, -) -def save_model_node(model: OnnxModel, directory: Path, model_name: str) -> None: - full_path = (directory / f"{model_name}.onnx").resolve() - logger.debug(f"Writing file to path: {full_path}") - full_path.parent.mkdir(parents=True, exist_ok=True) - with open(full_path, "wb") as f: - f.write(model.bytes) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/processing/remove_background.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/processing/remove_background.py deleted file mode 100644 index 1cd589a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/processing/remove_background.py +++ /dev/null @@ -1,89 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from api import NodeContext -from nodes.groups import Condition, if_group -from nodes.impl.onnx.model import OnnxRemBg -from nodes.impl.onnx.session import get_onnx_session -from nodes.impl.rembg.bg import remove_bg -from nodes.properties.inputs import ImageInput, OnnxRemBgModelInput -from nodes.properties.inputs.generic_inputs import BoolInput -from nodes.properties.inputs.numeric_inputs import NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from ...settings import get_settings -from .. import processing_group - - -@processing_group.register( - schema_id="chainner:onnx:rembg", - name="Remove Background", - description="""Removes background from image. - Currently supports u2net models from the rembg project (links found in readme).""", - icon="ONNX", - see_also="chainner:image:alpha_matting", - inputs=[ - ImageInput(channels=[3, 4]).with_docs( - "If the image has an alpha channel, it will be ignored." - ), - OnnxRemBgModelInput(), - BoolInput("Post-process Mask", default=False), - BoolInput("Alpha Matting", default=False), - if_group(Condition.bool(3, True))( - SliderInput("Foreground Threshold", minimum=1, maximum=255, default=240), - SliderInput("Background Threshold", maximum=254, default=10), - NumberInput("Erode Size", minimum=1, default=10), - ), - ], - outputs=[ - ImageOutput( - "Image", - image_type=""" - let image = Input0; - let model = Input1; - - Image { - width: image.width, - height: image.height * model.scaleHeight, - } - """, - channels=4, - ), - ImageOutput("Mask", image_type=navi.Image(size_as="Input0"), channels=1), - ], - node_context=True, -) -def remove_background_node( - context: NodeContext, - img: np.ndarray, - model: OnnxRemBg, - post_process_mask: bool, - alpha_matting: bool, - foreground_threshold: int, - background_threshold: int, - kernel_size: int, -) -> tuple[np.ndarray, np.ndarray]: - settings = get_settings(context) - session = get_onnx_session( - model, - settings.gpu_index, - settings.execution_provider, - settings.tensorrt_fp16_mode, - settings.tensorrt_cache_path, - ) - - # Remove alpha channel - if img.shape[2] == 4: - img = img[:, :, :3] - - return remove_bg( - img, - session, - alpha_matting, - foreground_threshold, - background_threshold, - alpha_matting_erode_size=kernel_size, - post_process_mask=bool(post_process_mask), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/processing/upscale_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/processing/upscale_image.py deleted file mode 100644 index 6bc11d6..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/processing/upscale_image.py +++ /dev/null @@ -1,153 +0,0 @@ -from __future__ import annotations - -import numpy as np -import onnxruntime as ort -from sanic.log import logger - -from api import NodeContext -from nodes.groups import Condition, if_enum_group, if_group -from nodes.impl.onnx.auto_split import onnx_auto_split -from nodes.impl.onnx.model import OnnxModel -from nodes.impl.onnx.session import get_onnx_session -from nodes.impl.onnx.utils import get_input_shape, get_output_shape -from nodes.impl.upscale.auto_split_tiles import ( - CUSTOM, - TILE_SIZE_256, - TileSize, - parse_tile_size_input, -) -from nodes.impl.upscale.convenient_upscale import convenient_upscale -from nodes.impl.upscale.tiler import ExactTileSize -from nodes.properties.inputs import ( - BoolInput, - ImageInput, - NumberInput, - OnnxGenericModelInput, - TileSizeDropdown, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from ...settings import get_settings -from .. import processing_group - - -def upscale( - img: np.ndarray, - session: ort.InferenceSession, - tile_size: TileSize, - change_shape: bool, - exact_size: tuple[int, int] | None, -) -> np.ndarray: - logger.debug("Upscaling image") - - if exact_size is None: - - def estimate(): - raise ValueError - - tiler = parse_tile_size_input(tile_size, estimate) - else: - tiler = ExactTileSize(exact_size) - - return onnx_auto_split(img, session, change_shape=change_shape, tiler=tiler) - - -@processing_group.register( - schema_id="chainner:onnx:upscale_image", - description=( - "Upscales an image using an ONNX Super-Resolution model. ONNX does" - " not support automatic out-of-memory handling via automatic tiling." - " Therefore, you must set a Smart Tiling Mode manually. If you get an" - " out-of-memory error, try picking a setting further down the list." - ), - inputs=[ - ImageInput().with_id(1), - OnnxGenericModelInput().with_id(0), - TileSizeDropdown(estimate=False, default=TILE_SIZE_256) - .with_id(2) - .with_docs( - "Tiled upscaling is used to allow large images to be upscaled without" - " hitting memory limits.", - "This works by splitting the image into tiles (with overlap), upscaling" - " each tile individually, and seamlessly recombining them.", - "Generally it's recommended to use the largest tile size possible for best" - " performance, but depending on the model and image size, this may not be" - " possible.", - "ONNX upscaling does not support an automatic mode, meaning you may need to" - " manually select a tile size for it to work.", - ), - if_enum_group(2, CUSTOM)( - NumberInput( - "Custom Tile Size", - minimum=1, - maximum=None, - default=TILE_SIZE_256, - precision=0, - controls_step=1, - unit="px", - has_handle=False, - ) - ), - if_group(Condition.type(1, "Image { channels: 4 } "))( - BoolInput("Separate Alpha", default=False).with_docs( - "Upscale alpha separately from color. Enabling this option will cause the alpha of" - " the upscaled image to be less noisy and more accurate to the alpha of the original" - " image, but the image may suffer from dark borders near transparency edges" - " (transition from fully transparent to fully opaque).", - "Whether enabling this option will improve the upscaled image depends on the original" - " image. We generally recommend this option for images with smooth transitions between" - " transparent and opaque regions.", - ) - ), - ], - outputs=[ImageOutput("Image")], - name="Upscale Image", - icon="ONNX", - node_context=True, -) -def upscale_image_node( - context: NodeContext, - img: np.ndarray, - model: OnnxModel, - tile_size: TileSize, - custom_tile_size: int | None, - separate_alpha: bool, -) -> np.ndarray: - settings = get_settings(context) - session = get_onnx_session( - model, - settings.gpu_index, - settings.execution_provider, - settings.tensorrt_fp16_mode, - settings.tensorrt_cache_path, - ) - - input_shape, in_nc, req_width, req_height = get_input_shape(session) - _, out_nc, _, _ = get_output_shape(session) - change_shape = input_shape == "BHWC" - - exact_size = None - if req_width is not None: - exact_size = req_width, req_height or req_width - elif req_height is not None: - exact_size = req_width or req_height, req_height - - h, w, c = get_h_w_c(img) - logger.debug(f"Image is {h}x{w}x{c}") - - return convenient_upscale( - img, - in_nc, - out_nc, - lambda i: upscale( - i, - session, - TileSize(custom_tile_size) - if tile_size == CUSTOM and custom_tile_size is not None - else tile_size, - change_shape, - exact_size, - ), - separate_alpha, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/convert_to_ncnn.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/convert_to_ncnn.py deleted file mode 100644 index bf6fc7f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/convert_to_ncnn.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import onnx - -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.impl.onnx.model import OnnxModel -from nodes.impl.onnx.onnx_to_ncnn import Onnx2NcnnConverter -from nodes.impl.onnx.utils import safely_optimize_onnx_model -from nodes.properties.inputs import OnnxFpDropdown, OnnxModelInput -from nodes.properties.outputs import NcnnModelOutput, TextOutput - -from .. import utility_group - -FP_MODE_32 = 0 - - -@utility_group.register( - schema_id="chainner:onnx:convert_to_ncnn", - name="Convert To NCNN", - description=[ - "Convert an ONNX model to NCNN.", - "It is recommended to save converted models as a separate step, then load the converted models instead of converting them every time you run the chain.", - "Note: Converted models are not guaranteed to work with other programs that support NCNN models. This is for a variety of reasons and cannot be changed.", - ], - icon="NCNN", - inputs=[OnnxModelInput("ONNX Model"), OnnxFpDropdown()], - outputs=[ - NcnnModelOutput(label="NCNN Model"), - TextOutput("FP Mode", "FpMode::toString(Input1)"), - ], -) -def convert_to_ncnn_node( - model: OnnxModel, is_fp16: int -) -> tuple[NcnnModelWrapper, str]: - fp16 = bool(is_fp16) - - model_proto = onnx.load_model_from_string(model.bytes) - opt_model = safely_optimize_onnx_model(model_proto) - - converter = Onnx2NcnnConverter(opt_model) - ncnn_model = NcnnModelWrapper(converter.convert(fp16, False)) - - fp_mode = "fp16" if fp16 else "fp32" - - return ncnn_model, fp_mode diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/interpolate_models.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/interpolate_models.py deleted file mode 100644 index a931ccb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/interpolate_models.py +++ /dev/null @@ -1,127 +0,0 @@ -from __future__ import annotations - -from copy import deepcopy - -import numpy as np -import onnx -from google.protobuf.internal.containers import RepeatedCompositeFieldContainer -from onnx import numpy_helper as onph -from onnx.onnx_pb import TensorProto -from sanic.log import logger - -from api import NodeContext -from nodes.impl.onnx.model import OnnxModel, load_onnx_model -from nodes.impl.onnx.utils import safely_optimize_onnx_model -from nodes.impl.upscale.auto_split_tiles import NO_TILING -from nodes.properties.inputs import OnnxModelInput, SliderInput -from nodes.properties.outputs import NumberOutput, OnnxModelOutput - -from .. import utility_group -from ..processing.upscale_image import upscale_image_node - - -def perform_interp( - model_a_weights: RepeatedCompositeFieldContainer, - model_b_weights: RepeatedCompositeFieldContainer, - amount: float, -) -> list[TensorProto]: - amount_b = amount / 100 - amount_a = 1 - amount_b - - interp_weights_list = [] - for weight_a, weight_b in zip(model_a_weights, model_b_weights): - weight_name = weight_b.name - weight_array_a = onph.to_array(weight_a) - weight_array_b = onph.to_array(weight_b) - - assert ( - weight_array_a.shape == weight_array_b.shape - ), "Weights must have same size and shape" - - weight_array_interp = ( - weight_array_a * amount_a + weight_array_b * amount_b - ).astype(weight_array_a.dtype) - weight_interp = onph.from_array(weight_array_interp, weight_name) - interp_weights_list.append(weight_interp) - - return interp_weights_list - - -def check_will_upscale(context: NodeContext, model: OnnxModel): - fake_img = np.ones((3, 3, 3), dtype=np.float32, order="F") - result = upscale_image_node(context, fake_img, model, NO_TILING, None, False) - - mean_color = np.mean(result) - del result - return mean_color > 0.5 - - -@utility_group.register( - schema_id="chainner:onnx:interpolate_models", - name="Interpolate Models", - description="Interpolate two ONNX models of the same type together. \ - Note: models must share a common 'pretrained model' ancestor \ - in order to be interpolatable.", - icon="BsTornado", - inputs=[ - OnnxModelInput("Model A"), - OnnxModelInput("Model B"), - SliderInput( - "Weights", - controls_step=5, - slider_step=1, - maximum=100, - default=50, - unit="%", - note_expression="`Model A ${100 - value}% ― Model B ${value}%`", - ends=("A", "B"), - ), - ], - outputs=[ - OnnxModelOutput(), - NumberOutput("Amount A", output_type="100 - Input2"), - NumberOutput("Amount B", output_type="Input2"), - ], - node_context=True, -) -def interpolate_models_node( - context: NodeContext, - a: OnnxModel, - b: OnnxModel, - amount: int, -) -> tuple[OnnxModel, int, int]: - if amount == 0: - return a, 100, 0 - elif amount == 100: - return b, 0, 100 - - # Just to be sure there is no mismatch from opt/un-opt models - model_proto_a = onnx.load_from_string(a.bytes) - model_proto_a = safely_optimize_onnx_model(model_proto_a) - model_a_weights = model_proto_a.graph.initializer - - model_proto_b = onnx.load_from_string(b.bytes) - model_proto_b = safely_optimize_onnx_model(model_proto_b) - model_b_weights = model_proto_b.graph.initializer - - assert len(model_a_weights) == len( - model_b_weights - ), "Models must have same number of weights" - - logger.debug("Interpolating models...") - interp_weights_list = perform_interp(model_a_weights, model_b_weights, amount) - - model_proto_interp = deepcopy(model_proto_b) - for _ in range(len(model_proto_interp.graph.initializer)): # type: ignore - # Assigning a new value or assigning to field index do not seem to work - model_proto_interp.graph.initializer.pop() # type: ignore - model_proto_interp.graph.initializer.extend(interp_weights_list) # type: ignore - model_interp = model_proto_interp.SerializeToString() # type: ignore - - model = load_onnx_model(model_interp) - if not check_will_upscale(context, model): - raise ValueError( - "These models are not compatible and not able to be interpolated together" - ) - - return model, 100 - amount, amount diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/optimize_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/optimize_model.py deleted file mode 100644 index 5df2fc7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/onnx/utility/optimize_model.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -import onnx - -from nodes.impl.onnx.model import OnnxModel, load_onnx_model -from nodes.impl.onnx.utils import safely_optimize_onnx_model -from nodes.properties.inputs import OnnxModelInput -from nodes.properties.outputs import OnnxModelOutput - -from .. import utility_group - - -@utility_group.register( - schema_id="chainner:onnx:optimize_model", - name="Optimize Model", - description="Optimize the give model. Optimizations may or may not improve performance.", - icon="MdSpeed", - inputs=[ - OnnxModelInput(), - ], - outputs=[ - OnnxModelOutput(), - ], -) -def optimize_model_node(model: OnnxModel) -> OnnxModel: - model_proto = onnx.load_from_string(model.bytes) - model_proto = safely_optimize_onnx_model(model_proto) - model_bytes = model_proto.SerializeToString() - return load_onnx_model(model_bytes) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/settings.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/settings.py deleted file mode 100644 index 08088b6..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_onnx/settings.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import annotations - -import os -from dataclasses import dataclass -from typing import List, cast - -import onnxruntime as ort -from sanic.log import logger - -from api import CacheSetting, DropdownSetting, NodeContext, ToggleSetting -from gpu import nvidia -from system import is_arm_mac - -from . import package - -if not is_arm_mac: - package.add_setting( - DropdownSetting( - label="GPU", - key="gpu_index", - description="Which GPU to use for ONNX. This is only relevant if you have multiple GPUs.", - options=[{"label": d.name, "value": str(d.index)} for d in nvidia.devices], - default="0", - ) - ) - - -def get_providers(): - providers = cast(List[str], ort.get_available_providers()) - - default = providers[0] - if "CUDAExecutionProvider" in providers: - default = "CUDAExecutionProvider" - elif "CPUExecutionProvider" in providers: - default = "CPUExecutionProvider" - - return providers, default - - -def get_provider_label(identifier: str) -> str: - label = identifier.replace("ExecutionProvider", "") - if label.lower() == "tensorrt": - label = "TensorRT" - return label - - -execution_providers, default_provider = get_providers() - -if not is_arm_mac: - package.add_setting( - DropdownSetting( - label="Execution Provider", - key="execution_provider", - description="What provider to use for ONNX.", - options=[ - {"label": get_provider_label(x), "value": x} - for x in execution_providers - ], - default=default_provider, - ) - ) - - package.add_setting( - CacheSetting( - label="Cache TensorRT Engines", - key="onnx_tensorrt_cache", - description="Whether to cache the converted TensorRT engines. This can significantly speed up subsequent runs.", - directory="onnx_tensorrt_cache", - disabled="TensorrtExecutionProvider" not in execution_providers, - ) - ) - - should_fp16 = nvidia.is_available and nvidia.all_support_fp16 - - package.add_setting( - ToggleSetting( - label="Use TensorRT FP16 Mode", - key="tensorrt_fp16_mode", - description="Runs TensorRT in half-precision (FP16) mode for less VRAM usage. RTX GPUs also get a speedup.", - default=should_fp16, - disabled="TensorrtExecutionProvider" not in execution_providers, - ) - ) - - -@dataclass(frozen=True) -class OnnxSettings: - gpu_index: int - execution_provider: str - tensorrt_cache_path: str | None - tensorrt_fp16_mode: bool - - -def get_settings(context: NodeContext) -> OnnxSettings: - settings = context.settings - - tensorrt_cache_path = settings.get_cache_location("onnx_tensorrt_cache") - logger.info(f"TensorRT cache location: {tensorrt_cache_path}") - if tensorrt_cache_path and not os.path.exists(tensorrt_cache_path): - os.makedirs(tensorrt_cache_path) - - return OnnxSettings( - gpu_index=settings.get_int("gpu_index", 0, parse_str=True), - execution_provider=settings.get_str("execution_provider", default_provider), - tensorrt_cache_path=tensorrt_cache_path, - tensorrt_fp16_mode=settings.get_bool("tensorrt_fp16_mode", False), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/io/save_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/io/save_model.py deleted file mode 100644 index ba4eb3c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/io/save_model.py +++ /dev/null @@ -1,62 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from pathlib import Path - -import torch -from safetensors.torch import save_file -from sanic.log import logger -from spandrel import ModelDescriptor - -from nodes.properties.inputs import ( - DirectoryInput, - EnumInput, - ModelInput, - RelativePathInput, -) - -from .. import io_group - - -class WeightFormat(Enum): - PTH = "pth" - ST = "safetensors" - - -@io_group.register( - schema_id="chainner:pytorch:save_model", - name="Save Model", - description=[ - "Save a PyTorch model to specified directory.", - 'This is not guaranteed to save certain models in a way that can be read by other programs. Notably, it saves without the "load key" some scripts expect.', - ], - icon="MdSave", - inputs=[ - ModelInput(), - DirectoryInput(must_exist=False), - RelativePathInput("Model Name"), - EnumInput( - WeightFormat, - "Weight Format", - default=WeightFormat.PTH, - option_labels={ - WeightFormat.PTH: "PyTorch (.pth)", - WeightFormat.ST: "SafeTensors (.safetensors)", - }, - ), - ], - outputs=[], - side_effects=True, -) -def save_model_node( - model: ModelDescriptor, directory: Path, name: str, weight_format: WeightFormat -) -> None: - full_path = (directory / f"{name}.{weight_format.value}").resolve() - logger.debug(f"Writing model to path: {full_path}") - full_path.parent.mkdir(parents=True, exist_ok=True) - if weight_format == WeightFormat.PTH: - torch.save(model.model.state_dict(), full_path) - elif weight_format == WeightFormat.ST: - save_file(model.model.state_dict(), full_path) - else: - raise ValueError(f"Unknown weight format: {weight_format}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/iteration/load_models.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/iteration/load_models.py deleted file mode 100644 index 1f0677e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/iteration/load_models.py +++ /dev/null @@ -1,65 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -from sanic.log import logger -from spandrel import ModelDescriptor - -from api import Iterator, IteratorOutputInfo, NodeContext -from nodes.properties.inputs import DirectoryInput -from nodes.properties.inputs.generic_inputs import BoolInput -from nodes.properties.outputs import DirectoryOutput, NumberOutput, TextOutput -from nodes.properties.outputs.pytorch_outputs import ModelOutput -from nodes.utils.utils import list_all_files_sorted - -from .. import batch_processing_group -from ..io.load_model import load_model_node - - -@batch_processing_group.register( - schema_id="chainner:pytorch:load_models", - name="Load Models", - description=( - "Iterate over all files in a directory and run the provided nodes on just the" - " PyTorch model files (.pth). Supports the same models as" - " `chainner:pytorch:load_model`." - ), - icon="MdLoop", - inputs=[ - DirectoryInput(), - BoolInput("Stop on first error", default=False).with_docs( - "Instead of collecting errors and throwing them at the end of processing, stop iteration and throw an error as soon as one occurs.", - hint=True, - ), - ], - outputs=[ - ModelOutput(), - DirectoryOutput("Directory", output_type="Input0"), - TextOutput("Subdirectory Path"), - TextOutput("Name"), - NumberOutput("Index", output_type="uint").with_docs( - "A counter that starts at 0 and increments by 1 for each model." - ), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 2, 3, 4]), - kind="newIterator", - node_context=True, -) -def load_models_node( - context: NodeContext, - directory: Path, - fail_fast: bool, -) -> tuple[Iterator[tuple[ModelDescriptor, str, str, int]], Path]: - logger.debug(f"Iterating over models in directory: {directory}") - - def load_model(path: Path, index: int): - model, dirname, basename = load_model_node(context, path) - # Get relative path from root directory passed by Iterator directory input - rel_path = os.path.relpath(dirname, directory) - return model, rel_path, basename, index - - supported_filetypes = [".pt", ".pth", ".ckpt", ".safetensors"] - model_files: list[Path] = list_all_files_sorted(directory, supported_filetypes) - - return Iterator.from_list(model_files, load_model, fail_fast), directory diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/align_image_to_reference.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/align_image_to_reference.py deleted file mode 100644 index 95c7889..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/align_image_to_reference.py +++ /dev/null @@ -1,242 +0,0 @@ -# Original Rife Frame Interpolation by hzwer -# https://github.com/megvii-research/ECCV2022-RIFE -# https://github.com/hzwer/Practical-RIFE - -# Modifications to use Rife for Image Alignment by tepete/pifroggi ('Enhance Everything!' Discord Server) - -# Additional helpful github issues -# https://github.com/megvii-research/ECCV2022-RIFE/issues/278 -# https://github.com/megvii-research/ECCV2022-RIFE/issues/344 - -import os -import zipfile -from enum import Enum -from pathlib import Path - -import numpy as np -import requests -import torch -from appdirs import user_data_dir - -from api import NodeContext -from nodes.impl.pytorch.rife.IFNet_HDv3_v4_14_align import ( - IFNet, -) -from nodes.impl.pytorch.utils import np2tensor, tensor2np -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from ...settings import get_settings -from .. import processing_group - - -class PrecisionMode(Enum): - FIFTY_PERCENT = 2000 - ONE_HUNDRED_PERCENT = 1000 - TWO_HUNDRED_PERCENT = 500 - FOUR_HUNDRED_PERCENT = 250 - EIGHT_HUNDRED_PERCENT = 125 - - -def calculate_padding(height, width, precision_mode): # noqa: ANN001 - if precision_mode == PrecisionMode.EIGHT_HUNDRED_PERCENT: - pad_value = 4 - elif precision_mode == PrecisionMode.FOUR_HUNDRED_PERCENT: - pad_value = 8 - elif precision_mode == PrecisionMode.TWO_HUNDRED_PERCENT: - pad_value = 16 - elif precision_mode == PrecisionMode.ONE_HUNDRED_PERCENT: - pad_value = 32 - else: - pad_value = 64 - - pad_height = (pad_value - height % pad_value) % pad_value - pad_width = (pad_value - width % pad_value) % pad_value - return pad_height, pad_width - - -def download_model(download_url, download_path, model_file, zip_inner_path): # noqa: ANN001 - model_dir = Path(download_path) - model_dir.mkdir(parents=True, exist_ok=True) - zip_path = model_dir / "model.zip" - - if not (model_dir / model_file).exists(): - try: - response = requests.get(download_url) - response.raise_for_status() - with open(zip_path, "wb") as f: - f.write(response.content) - with zipfile.ZipFile(zip_path, "r") as zip_ref: - specific_file_path = zip_inner_path + "/" + model_file - zip_ref.extract(specific_file_path, model_dir) - extracted_file_path = model_dir / specific_file_path - final_path = model_dir / model_file - if extracted_file_path != final_path: - extracted_file_path.rename(final_path) - - # cleanup empty folder - train_log_dir = model_dir / zip_inner_path - train_log_dir.rmdir() - - zip_path.unlink() - except requests.RequestException as e: - print(f"Failed to download the model. Error: {e}") - - -def align_images( - context, # noqa: ANN001 - target_img: np.ndarray, - source_img: np.ndarray, - precision_mode, # noqa: ANN001 - model_file="flownet.pkl", # noqa: ANN001 - multiplier=1, # noqa: ANN001 - alignment_passes=1, # noqa: ANN001 - blur_strength=0, # noqa: ANN001 - ensemble=True, # noqa: ANN001 -) -> np.ndarray: - appdata_path = user_data_dir(roaming=True) - path_str = "chaiNNer/python/rife_v4.14/weights" - download_path = os.path.join(appdata_path, path_str) - download_url = "https://drive.usercontent.google.com/download?id=1BjuEY7CHZv1wzmwXSQP9ZTj0mLWu_4xy&export=download&authuser=0" - zip_inner_path = "train_log" - download_model(download_url, download_path, model_file, zip_inner_path) - - source_h, source_w, _ = get_h_w_c(source_img) - target_h, target_w, _ = get_h_w_c(target_img) # type: ignore - - # resize, then shift reference left because rife shifts slightly to the right - target_img_resized = resize( - target_img, (source_w, source_h), filter=ResizeFilter.LANCZOS - ) - target_img_resized = np.roll(target_img_resized, -1, axis=1) - target_img_resized[:, -1] = target_img_resized[:, -2] - - # padding because rife can only work with multiples of 32 (changes with precision mode) - pad_h, pad_w = calculate_padding(source_h, source_w, precision_mode) - top_pad = pad_h // 2 - bottom_pad = pad_h - top_pad - left_pad = pad_w // 2 - right_pad = pad_w - left_pad - target_img_padded = np.pad( - target_img_resized, - ((top_pad, bottom_pad), (left_pad, right_pad), (0, 0)), - mode="edge", - ) - source_img_padded = np.pad( - source_img, ((top_pad, bottom_pad), (left_pad, right_pad), (0, 0)), mode="edge" - ) - - exec_options = get_settings(context) - device = exec_options.device - - # load model - model_full_path = os.path.join(download_path, model_file) - model = IFNet().to(device) - state_dict = torch.load(model_full_path, map_location=device) - new_state_dict = {k.replace("module.", ""): v for k, v in state_dict.items()} - model.load_state_dict(new_state_dict) - model.eval() - - # convert to tensors - target_tensor_padded = np2tensor(target_img_padded, change_range=True).to(device) - source_tensor_padded = np2tensor(source_img_padded, change_range=True).to(device) - - # concatenate images - img_pair = torch.cat((target_tensor_padded, source_tensor_padded), dim=1) - - with torch.no_grad(): - aligned_img, _ = model( - img_pair, - multiplier=multiplier, - num_iterations=alignment_passes, - blur_strength=blur_strength, - ensemble=ensemble, - device=device, - ) - - # convert back to numpy and crop - result_img = tensor2np( - aligned_img.squeeze(0).cpu(), change_range=False, imtype=np.float32 - ) - result_img = result_img[ - top_pad : top_pad + source_h, left_pad : left_pad + source_w - ] - - return result_img - - -@processing_group.register( - schema_id="chainner:pytorch:image_align_rife", - name="Align Image to Reference", - description="Aligns an Image with a Reference Image using Rife. Images should have vague alignment before using this Node. Output Image will have the same dimensions as Reference Image. Resize Reference Image to get desired output scale.", - icon="BsRulers", - inputs=[ - ImageInput(label="Image", channels=3), - ImageInput(label="Reference Image", channels=3), - EnumInput( - PrecisionMode, - label="Precision", - default=PrecisionMode.ONE_HUNDRED_PERCENT, - option_labels={ - PrecisionMode.FIFTY_PERCENT: "50%", - PrecisionMode.ONE_HUNDRED_PERCENT: "100%", - PrecisionMode.TWO_HUNDRED_PERCENT: "200%", - PrecisionMode.FOUR_HUNDRED_PERCENT: "400%", - PrecisionMode.EIGHT_HUNDRED_PERCENT: "800% (VRAM!)", - }, - ).with_docs( - "If the Alignment is very close, try a **high** value.", - "If the Alignment is **not** very close, try a **low** value.", - "Higher values will internally align at higher resolutions to increase precision, which will in turn increase processing time and VRAM usage. Lower values are less precise, but can align over larger distances.", - hint=True, - ), - NumberInput( - "Alignment Passes", - controls_step=1, - minimum=1, - maximum=1000, - default=1, - unit="#", - ).with_docs( - "Runs the alignment multiple times.", - "With more than around 4 passes, artifacts can appear. Try to keep it low.", - hint=True, - ), - NumberInput( - "Blur Strength", - minimum=0, - maximum=100, - default=0, - precision=1, - controls_step=1, - unit="⌀", - ).with_docs( - "Blur is only used internally and will not be visible on the Output Image. It will reduce accuracy, try to keep it **low**. The **best** alignment will be at **Blur 0**.", - "Blur can help to ignore strong degredations (like compression or noise). If the lines on the Output Image get thinner or thicker, try to increase the blur a little as well.", - hint=True, - ), - ], - outputs=[ImageOutput().with_never_reason("Returns the aligned image.")], - node_context=True, -) -def align_image_to_reference_node( - context: NodeContext, - target_img: np.ndarray, - source_img: np.ndarray, - precision: PrecisionMode, - alignment_passes: int, - blur_strength: float, -) -> np.ndarray: - multiplier = precision.value / 1000 - return align_images( - context, - target_img, - source_img, - precision, - multiplier=multiplier, # type: ignore - alignment_passes=alignment_passes, - blur_strength=blur_strength, # type: ignore - ensemble=1, # type: ignore - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/guided_upscale.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/guided_upscale.py deleted file mode 100644 index 0e67eae..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/guided_upscale.py +++ /dev/null @@ -1,87 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from api import NodeContext -from nodes.impl.pytorch.pix_transform.auto_split import pix_transform_auto_split -from nodes.impl.pytorch.pix_transform.pix_transform import Params -from nodes.impl.upscale.grayscale import SplitMode -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from ...settings import get_settings -from .. import processing_group - - -@processing_group.register( - schema_id="chainner:pytorch:guided_upscale", - name="Guided Upscale", - description=( - "Upscales a source image using a guide." - " This is most useful for very small source images." - "\n\nUnder the hood, PixTransform is used which trains a NN to convert the guide image into the source image." - " Note that this operation is very expensive, because it needs to train a NN." - " Try a small number of iterations before going up to around 30k." - ), - icon="PyTorch", - inputs=[ - ImageInput("Source"), - ImageInput("Guide"), - SliderInput( - "Iterations", - minimum=0.1, - maximum=100, - default=1, - precision=1, - scale="log", - unit="k", - ), - EnumInput( - SplitMode, - "Channel split mode", - SplitMode.LAB, - option_labels={SplitMode.RGB: "RGB", SplitMode.LAB: "L*a*b"}, - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let source = Input0; - let guide = Input1; - - let kScale = ( - // guide image's size must be `k * source.size` for `k>1` - guide.width / source.width == int - and guide.width / source.width == guide.height / source.height - ); - - if guide.width <= source.width { - error("The guide image must be larger than the source image.") - } else if not kScale { - error("The size of the guide image must be an integer multiple of the size of the source image (e.g. 2x, 3x, 4x, ...).") - } else { - Image { - width: guide.width, - height: guide.height, - channels: source.channels, - } - } - """ - ), - ], - node_context=True, -) -def guided_upscale_node( - context: NodeContext, - source: np.ndarray, - guide: np.ndarray, - iterations: float, - split_mode: SplitMode, -) -> np.ndarray: - return pix_transform_auto_split( - source=source, - guide=guide, - device=get_settings(context).device, - params=Params(iteration=int(iterations * 1000)), - split_mode=split_mode, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/inpaint.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/inpaint.py deleted file mode 100644 index 761821b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/inpaint.py +++ /dev/null @@ -1,118 +0,0 @@ -from __future__ import annotations - -import gc - -import numpy as np -import torch -from spandrel import MaskedImageModelDescriptor - -import navi -from api import NodeContext -from nodes.impl.pytorch.utils import np2tensor, safe_cuda_cache_empty, tensor2np -from nodes.properties.inputs import ImageInput -from nodes.properties.inputs.pytorch_inputs import InpaintModelInput -from nodes.properties.outputs import ImageOutput - -from ...settings import PyTorchSettings, get_settings -from .. import processing_group - - -def inpaint( - img: np.ndarray, - mask: np.ndarray, - model: MaskedImageModelDescriptor, - options: PyTorchSettings, -): - with torch.no_grad(): - # TODO: use bfloat16 if RTX - use_fp16 = options.use_fp16 and model.supports_half - dtype = torch.float16 if use_fp16 else torch.float32 - device = options.device - - model = model.to(device, dtype) - - img_tensor = np2tensor(img, change_range=True) - mask_tensor = np2tensor(mask, change_range=True) - - d_img = None - d_mask = None - try: - d_img = img_tensor.to(device, dtype) - - d_mask = mask_tensor.to(device, dtype) - d_mask = (d_mask > 0.5) * 1 - d_mask = d_mask.to(dtype) - - result = model(d_img, d_mask) - result = tensor2np( - result.detach().cpu().detach(), - change_range=False, - imtype=np.float32, - ) - - del d_img - del d_mask - - return result - except RuntimeError: - # Collect garbage (clear VRAM) - if d_img is not None: - try: - d_img.detach().cpu() - except Exception: - pass - del d_img - if d_mask is not None: - try: - d_mask.detach().cpu() - except Exception: - pass - del d_mask - gc.collect() - safe_cuda_cache_empty() - - raise - - -@processing_group.register( - schema_id="chainner:pytorch:inpaint", - name="Inpaint", - description=[ - "Inpaint an image with given mask, using a PyTorch inpainting model.", - "Masks must typically be made outside of chaiNNer.", - "Supported models include LaMa and MAT", - ], - icon="PyTorch", - inputs=[ - ImageInput(channels=3), - ImageInput(label="Mask", channels=1).with_docs( - "An inpainting mask is a grayscale image where white represents what to inpaint and black represents what to keep.", - "This must typically be made outside of chaiNNer.", - hint=True, - ), - InpaintModelInput(), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input0.width & Input1.width", - height="Input0.height & Input1.height", - ), - channels=3, - ).with_never_reason("The given image and mask must have the same resolution.") - ], - node_context=True, -) -def inpaint_node( - context: NodeContext, - img: np.ndarray, - mask: np.ndarray, - model: MaskedImageModelDescriptor, -) -> np.ndarray: - assert ( - img.shape[:2] == mask.shape[:2] - ), "Input image and mask must have the same resolution" - - exec_options = get_settings(context) - - return inpaint(img, mask, model, exec_options) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/wavelet_color_fix.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/wavelet_color_fix.py deleted file mode 100644 index 39730af..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/processing/wavelet_color_fix.py +++ /dev/null @@ -1,108 +0,0 @@ -# Wavelet color fix from "sd-webui-stablesr" https://github.com/pkuliyi2015/sd-webui-stablesr/blob/master/srmodule/colorfix.py - -# Node by tepete/pifroggi ('Enhance Everything!' Discord Server) - -import numpy as np -import torch -import torch.nn.functional as F # noqa: N812 - -from api import NodeContext -from nodes.impl.pytorch.utils import np2tensor, tensor2np -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from ...settings import get_settings -from .. import processing_group - - -def wavelet_blur(image: torch.Tensor, radius: int) -> torch.Tensor: - kernel_vals = [ - [0.0625, 0.125, 0.0625], - [0.125, 0.25, 0.125], - [0.0625, 0.125, 0.0625], - ] - kernel = torch.tensor(kernel_vals, dtype=image.dtype, device=image.device) - kernel = kernel[None, None].repeat(3, 1, 1, 1) - image = F.pad(image, (radius, radius, radius, radius), mode="replicate") - output = F.conv2d(image, kernel, groups=3, dilation=radius) - return output - - -def wavelet_decomposition(image: torch.Tensor, levels: int = 5) -> tuple: - high_freq = torch.zeros_like(image) - low_freq = None - for i in range(levels): - radius = 2**i - low_freq = wavelet_blur(image, radius) - high_freq += image - low_freq - image = low_freq - return high_freq, low_freq - - -def wavelet_reconstruction( - content_feat: torch.Tensor, style_feat: torch.Tensor, levels: int -) -> torch.Tensor: - content_high_freq, content_low_freq = wavelet_decomposition( # type: ignore - content_feat, levels=levels - ) - style_high_freq, style_low_freq = wavelet_decomposition(style_feat, levels=levels) # type: ignore - return content_high_freq + style_low_freq - - -@processing_group.register( - schema_id="chainner:pytorch:wavelet_color_fix", - name="Wavelet Color Fix", - description=[ - "Correct for upscaling model color shift by first separating the image into wavelets of different frequencies, then matching the average color of the Input Image to that of a Reference Image. In general produces better results than the Average Color Fix at the cost of more computation." - ], - icon="MdAutoFixHigh", - inputs=[ - ImageInput(label="Image", channels=3), - ImageInput(label="Reference Image", channels=3), - NumberInput( - "Number of Wavelets", - controls_step=1, - minimum=1, - maximum=10, - default=5, - unit="#", - ).with_docs( - "Around 5 seems to work best in most cases.", - "**Higher** means a more global color match. Wider bloom/bleed and less local color precision.", - "**Lower** means a more local color match. Smaller bloom/bleed and more artifacts. Too low and the reference image will become visible.", - hint=True, - ), - ], - outputs=[ImageOutput().with_never_reason("Returns the color-fixed image.")], - node_context=True, -) -def wavelet_color_fix_node( - context: NodeContext, target_img: np.ndarray, source_img: np.ndarray, levels: int -) -> np.ndarray: - target_h, target_w, _ = get_h_w_c(target_img) - - # resize source image to match target image - source_img_resized = resize( - source_img, (target_w, target_h), filter=ResizeFilter.BOX - ) - - exec_options = get_settings(context) - device = exec_options.device - - # convert to tensors - target_tensor = np2tensor(target_img, change_range=True).to(device) - source_tensor_resized = np2tensor(source_img_resized, change_range=True).to(device) - - # wavelet color fix - result_tensor = wavelet_reconstruction( - target_tensor, source_tensor_resized, levels=levels - ) - - # convert back to numpy array - result_img = tensor2np( - result_tensor.detach().cpu(), change_range=False, imtype=np.float32 - ) - - return result_img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/restoration/upscale_face.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/restoration/upscale_face.py deleted file mode 100644 index 9b04d7f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/restoration/upscale_face.py +++ /dev/null @@ -1,197 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np -import torch -from facexlib.utils.face_restoration_helper import FaceRestoreHelper -from sanic.log import logger -from spandrel import ImageModelDescriptor -from torchvision.transforms.functional import normalize as tv_normalize - -from api import KeyInfo, NodeContext -from nodes.groups import Condition, if_group -from nodes.impl.image_utils import to_uint8 -from nodes.impl.pytorch.utils import np2tensor, safe_cuda_cache_empty, tensor2np -from nodes.properties.inputs import FaceModelInput, ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from ...settings import PyTorchSettings, get_settings -from .. import restoration_group - - -def denormalize(img: np.ndarray): - img = to_uint8(img, normalized=True) - _, _, c = get_h_w_c(img) - if c == 4: - img = img[:, :, :3] - elif c == 1: - img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) - return img - - -@torch.inference_mode() -def upscale( - img: np.ndarray, - background_img: np.ndarray | None, - face_helper: FaceRestoreHelper, - face_model: ImageModelDescriptor, - weight: float, - exec_options: PyTorchSettings, - device: torch.device, -): - face_helper.clean_all() - - face_helper.read_image(img) - # get face landmarks for each face - face_helper.get_face_landmarks_5(only_center_face=False, eye_dist_threshold=5) - # eye_dist_threshold=5: skip faces whose eye distance is smaller than 5 pixels - # TODO: even with eye_dist_threshold, it will still introduce wrong detections and restorations. - # align and warp each face - face_helper.align_warp_face() - - should_use_fp16 = exec_options.use_fp16 and face_model.supports_half - if should_use_fp16: - face_model.model.half() - else: - face_model.model.float() - - # face restoration - for cropped_face in face_helper.cropped_faces: - # prepare data - cropped_face_t = np2tensor( - cropped_face, bgr2rgb=True, change_range=True, add_batch=False - ) - tv_normalize(cropped_face_t, [0.5, 0.5, 0.5], [0.5, 0.5, 0.5], inplace=True) - cropped_face_t = cropped_face_t.unsqueeze(0).to(device) - - try: - if should_use_fp16: - cropped_face_t = cropped_face_t.half() - else: - cropped_face_t = cropped_face_t.float() - output = face_model.model(cropped_face_t, return_rgb=False, weight=weight)[ - 0 - ] - # convert to image - output = (output + 1) / 2 - restored_face = tensor2np(output.squeeze(0), rgb2bgr=True) - except RuntimeError as error: - logger.error(f"\tFailed inference for Face Upscale: {error}.") - restored_face = cropped_face - - restored_face = restored_face.astype("uint8") - face_helper.add_restored_face(restored_face) - - if background_img is not None: - # upsample the background - background_img = denormalize(background_img) - - face_helper.get_inverse_affine(None) - # paste each restored face to the input image - restored_img = face_helper.paste_faces_to_input_image( - upsample_img=background_img - ) - else: - face_helper.get_inverse_affine(None) - restored_img = face_helper.paste_faces_to_input_image() - del face_helper - safe_cuda_cache_empty() - - restored_img = np.clip(restored_img.astype("float32") / 255.0, 0, 1) - - return restored_img - - -@restoration_group.register( - schema_id="chainner:pytorch:upscale_face", - name="Upscale Face", - description=( - "Uses face-detection to upscales and restore face(s) in an image using a" - " PyTorch Face Super-Resolution model. Right now supports GFPGAN," - " RestoreFormer, and CodeFormer." - ), - icon="PyTorch", - inputs=[ - ImageInput().with_id(1), - FaceModelInput("Model").with_id(0), - ImageInput("Upscaled Background").with_id(2).make_optional(), - NumberInput( - label="Output Scale", default=8, minimum=1, maximum=8, unit="x" - ).with_id(3), - if_group(Condition.type(0, 'PyTorchModel { arch: "CodeFormer" }'))( - SliderInput( - label="Weight", - default=0.7, - minimum=0.0, - maximum=1.0, - controls_step=0.1, - slider_step=0.1, - precision=1, - ) - ), - ], - outputs=[ - ImageOutput( - "Image", - image_type=""" - Image { - width: Input3 * Input1.width, - height: Input3 * Input1.height, - } - """, - channels=3, - ) - ], - key_info=KeyInfo.number(3), - limited_to_8bpc=True, - node_context=True, -) -def upscale_face_node( - context: NodeContext, - img: np.ndarray, - face_model: ImageModelDescriptor, - background_img: np.ndarray | None, - scale: int, - weight: float, -) -> np.ndarray: - face_helper = None - try: - img = denormalize(img) - - exec_options = get_settings(context) - device = exec_options.device - - with torch.no_grad(): - download_path = str(context.storage_dir / "gfpgan/weights") - - # initialize face helper - face_helper = FaceRestoreHelper( - upscale_factor=scale, - face_size=512, - crop_ratio=(1, 1), - det_model="retinaface_resnet50", - save_ext="png", - use_parse=True, - device=device, - model_rootpath=download_path, - ) - - result = upscale( - img, - background_img, - face_helper, - face_model, - weight, - exec_options, - device, - ) - - return result - - except Exception as e: - logger.error(f"Face Upscale failed: {e}") - face_helper = None - del face_helper - safe_cuda_cache_empty() - raise RuntimeError("Failed to run Face Upscale.") from e diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/convert_to_ncnn.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/convert_to_ncnn.py deleted file mode 100644 index 90df1c9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/convert_to_ncnn.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import annotations - -from spandrel import ImageModelDescriptor -from spandrel.architectures.DAT import DAT -from spandrel.architectures.HAT import HAT -from spandrel.architectures.OmniSR import OmniSR -from spandrel.architectures.SCUNet import SCUNet -from spandrel.architectures.SPAN import SPAN -from spandrel.architectures.Swin2SR import Swin2SR -from spandrel.architectures.SwinIR import SwinIR -from spandrel_extra_arches.architectures.SRFormer import SRFormer - -from api import NodeContext -from nodes.impl.ncnn.model import NcnnModelWrapper -from nodes.impl.onnx.model import OnnxGeneric -from nodes.impl.pytorch.convert_to_onnx_impl import ( - convert_to_onnx_impl, - is_onnx_supported, -) -from nodes.properties.inputs import OnnxFpDropdown, SrModelInput -from nodes.properties.outputs import NcnnModelOutput, TextOutput - -from ...settings import get_settings -from .. import utility_group - -try: - from ....chaiNNer_onnx.onnx.utility.convert_to_ncnn import ( - convert_to_ncnn_node as onnx_convert_to_ncnn_node, - ) -except Exception: - onnx_convert_to_ncnn_node = None - - -@utility_group.register( - schema_id="chainner:pytorch:convert_to_ncnn", - name="Convert To NCNN", - description=[ - "Convert a PyTorch model to NCNN. Internally, this node uses ONNX as an intermediate format, so the ONNX dependency must also be installed to use this node.", - "It is recommended to save converted models as a separate step, then load the converted models instead of converting them every time you run the chain.", - "Note: Converted models are not guaranteed to work with other programs that support NCNN models. This is for a variety of reasons and cannot be changed.", - ], - icon="NCNN", - inputs=[ - SrModelInput("PyTorch Model"), - OnnxFpDropdown(), - ], - outputs=[ - NcnnModelOutput(label="NCNN Model"), - TextOutput("FP Mode", "FpMode::toString(Input1)"), - ], - node_context=True, -) -def convert_to_ncnn_node( - context: NodeContext, model: ImageModelDescriptor, is_fp16: int -) -> tuple[NcnnModelWrapper, str]: - if onnx_convert_to_ncnn_node is None: - raise ModuleNotFoundError( - "Converting to NCNN is done through ONNX as an intermediate format (PyTorch -> ONNX -> NCNN), \ - and therefore requires the ONNX dependency to be installed. Please install ONNX through the dependency \ - manager to use this node." - ) - - assert is_onnx_supported(model) and not isinstance( - model.model, (HAT, DAT, OmniSR, SwinIR, Swin2SR, SCUNet, SPAN, SRFormer) - ), f"{model.architecture.name} is not supported for NCNN conversions at this time." - - exec_options = get_settings(context) - device = exec_options.device - - # Intermediate conversion to ONNX is always fp32 - onnx_model = OnnxGeneric( - convert_to_onnx_impl(model, device, False, "data", "output") - ) - ncnn_model, fp_mode = onnx_convert_to_ncnn_node(onnx_model, is_fp16) - - return ncnn_model, fp_mode diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/convert_to_onnx.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/convert_to_onnx.py deleted file mode 100644 index 4b86710..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/convert_to_onnx.py +++ /dev/null @@ -1,98 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from spandrel import ImageModelDescriptor - -from api import NodeContext -from nodes.impl.onnx.model import OnnxGeneric -from nodes.impl.pytorch.convert_to_onnx_impl import ( - convert_to_onnx_impl, - is_onnx_supported, -) -from nodes.properties.inputs import EnumInput, OnnxFpDropdown, SrModelInput -from nodes.properties.outputs import OnnxModelOutput, TextOutput - -from ...settings import get_settings -from .. import utility_group - - -class Opset(Enum): - OPSET_14 = 14 - OPSET_15 = 15 - OPSET_16 = 16 - OPSET_17 = 17 - - -OPSET_LABELS: dict[Opset, str] = { - Opset.OPSET_14: "14", - Opset.OPSET_15: "15", - Opset.OPSET_16: "16", - Opset.OPSET_17: "17", -} - - -@utility_group.register( - schema_id="chainner:pytorch:convert_to_onnx", - name="Convert To ONNX", - description=[ - "Convert a PyTorch model to ONNX. Note: fp16 conversion will only work if PyTorch fp16 mode is turned on.", - "It is recommended to save converted models as a separate step, then load the converted models instead of converting them every time you run the chain.", - "Note: Converted models are not guaranteed to work with other programs that support ONNX models. This is for a variety of reasons and cannot be changed.", - ], - icon="ONNX", - inputs=[ - SrModelInput("PyTorch Model"), - OnnxFpDropdown(), - EnumInput( - Opset, - label="Opset", - default=Opset.OPSET_14, - option_labels=OPSET_LABELS, - ), - ], - outputs=[ - OnnxModelOutput(model_type="OnnxGenericModel", label="ONNX Model"), - TextOutput("FP Mode", "FpMode::toString(Input1)"), - TextOutput( - "Opset", - """ - let opset = Input2; - match opset { - Opset::Opset14 => "opset14", - Opset::Opset15 => "opset15", - Opset::Opset16 => "opset16", - Opset::Opset17 => "opset17", - } - """, - ), - ], - node_context=True, -) -def convert_to_onnx_node( - context: NodeContext, model: ImageModelDescriptor, is_fp16: int, opset: Opset -) -> tuple[OnnxGeneric, str, str]: - assert is_onnx_supported( - model - ), f"{model.architecture} is not supported for ONNX conversion at this time." - - fp16 = bool(is_fp16) - exec_options = get_settings(context) - device = exec_options.device - if fp16: - assert exec_options.use_fp16, "PyTorch fp16 mode must be supported and turned on in settings to convert model as fp16." - - model.eval().to(device) - - use_half = fp16 and model.supports_half - - onnx_model_bytes = convert_to_onnx_impl( - model, - device, - use_half, - opset_version=opset.value, - ) - - fp_mode = "fp16" if use_half else "fp32" - - return OnnxGeneric(onnx_model_bytes), fp_mode, f"opset{opset.value}" diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/get_model_scale.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/get_model_scale.py deleted file mode 100644 index 77297da..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/get_model_scale.py +++ /dev/null @@ -1,20 +0,0 @@ -from __future__ import annotations - -from spandrel import ModelDescriptor - -from nodes.properties.inputs import ModelInput -from nodes.properties.outputs import NumberOutput - -from .. import utility_group - - -@utility_group.register( - schema_id="chainner:pytorch:model_dim", - name="Get Model Scale", - description="""Returns the scale of a PyTorch model.""", - icon="BsRulers", - inputs=[ModelInput()], - outputs=[NumberOutput("Scale", output_type="Input0.scale")], -) -def get_model_scale_node(model: ModelDescriptor) -> int: - return model.scale diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/interpolate_models.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/interpolate_models.py deleted file mode 100644 index 3ac1a98..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_pytorch/pytorch/utility/interpolate_models.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import annotations - -import gc - -import numpy as np -import torch -from sanic.log import logger -from spandrel import ( - ImageModelDescriptor, - MaskedImageModelDescriptor, - ModelDescriptor, - ModelLoader, -) - -from api.node_context import NodeContext -from nodes.impl.pytorch.utils import np2tensor, tensor2np -from nodes.properties.inputs import ModelInput, SliderInput -from nodes.properties.outputs import ModelOutput, NumberOutput -from packages.chaiNNer_pytorch.settings import get_settings - -from .. import utility_group - - -def perform_interp(model_a: dict, model_b: dict, amount: int): - try: - amount_b = amount / 100 - amount_a = 1 - amount_b - - state_dict = {} - for k, v_1 in model_a.items(): - v_2 = model_b[k] - state_dict[k] = (amount_a * v_1) + (amount_b * v_2) - return state_dict - except Exception as e: - raise ValueError( - "These models are not compatible and able not able to be interpolated together" - ) from e - - -def check_can_interp(model_a: dict, model_b: dict): - a_keys = model_a.keys() - b_keys = model_b.keys() - if a_keys != b_keys: - return False - interp_50 = perform_interp(model_a, model_b, 50) - model_descriptor = ModelLoader(torch.device("cpu")).load_from_state_dict(interp_50) - size = max(model_descriptor.size_requirements.minimum, 3) - size = size + (size % model_descriptor.size_requirements.multiple_of) - assert isinstance(size, int), "min_size_restriction must be an int" - fake_img = np.ones((size, size, model_descriptor.input_channels), dtype=np.float32) - del interp_50 - with torch.no_grad(): - img_tensor = np2tensor(fake_img, change_range=True).cpu() - if isinstance(model_descriptor, MaskedImageModelDescriptor): - np.ones((size, size, 1), dtype=np.float32) - mask_tensor = np2tensor(fake_img, change_range=True).cpu() - t_out = model_descriptor(img_tensor, mask_tensor) - elif isinstance(model_descriptor, ImageModelDescriptor): # type: ignore <- I get that this can technically never happen, but please just let me write exhaustive checks - t_out = model_descriptor(img_tensor) - else: - logger.warning( - "Unknown model type used with interpolation. Since we cannot verify inference works with this model, we will assume the interpolation is valid. Please report." - ) - return True - if isinstance(t_out, tuple): - t_out = t_out[0] - result = tensor2np(t_out.detach(), change_range=False, imtype=np.float32) - del model_descriptor, img_tensor, t_out, fake_img - mean_color = np.mean(result) - del result - gc.collect() - return mean_color > 0.5 - - -@utility_group.register( - schema_id="chainner:pytorch:interpolate_models", - name="Interpolate Models", - description="""Interpolate two of the same kind of model state-dict - together. Note: models must share a common 'pretrained model' ancestor - in order to be interpolatable.""", - icon="BsTornado", - inputs=[ - ModelInput("Model A"), - ModelInput("Model B"), - SliderInput( - "Weights", - controls_step=5, - slider_step=1, - maximum=100, - default=50, - unit="%", - note_expression="`Model A ${100 - value}% ― Model B ${value}%`", - ends=("A", "B"), - ), - ], - outputs=[ - ModelOutput(model_type="Input0 & Input1").with_never_reason( - "Models must be of the same type and have the same parameters to be interpolated." - ), - NumberOutput("Amount A", output_type="100 - Input2"), - NumberOutput("Amount B", output_type="Input2"), - ], - node_context=True, -) -def interpolate_models_node( - context: NodeContext, - model_a: ModelDescriptor, - model_b: ModelDescriptor, - amount: int, -) -> tuple[ModelDescriptor, int, int]: - exec_options = get_settings(context) - pytorch_device = exec_options.device - - if amount == 0: - return model_a, 100, 0 - elif amount == 100: - return model_b, 0, 100 - - if model_a.device != model_b.device: - model_a.to(pytorch_device) - model_b.to(pytorch_device) - - state_a = model_a.model.state_dict() - state_b = model_b.model.state_dict() - - logger.debug("Interpolating models...") - if not check_can_interp(state_a, state_b): - raise ValueError( - "These models are not compatible and not able to be interpolated together" - ) - - state_dict = perform_interp(state_a, state_b, amount) - - model = ModelLoader(pytorch_device).load_from_state_dict(state_dict) - - return model, 100 - amount, amount diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/__init__.py deleted file mode 100644 index bd9b0bc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/__init__.py +++ /dev/null @@ -1,151 +0,0 @@ -from sanic.log import logger - -from api import KB, MB, Dependency, add_package - -package = add_package( - __file__, - id="chaiNNer_standard", - name="chaiNNer_standard", - description="The standard set of nodes for chaiNNer.", - dependencies=[ - Dependency( - display_name="Numpy", - pypi_name="numpy", - version="1.24.4", - size_estimate=15 * MB, - ), - Dependency( - display_name="OpenCV", - pypi_name="opencv-python", - version="4.8.0.76", - size_estimate=30 * MB, - import_name="cv2", - ), - Dependency( - display_name="Pillow (PIL)", - pypi_name="Pillow", - version="9.2.0", - size_estimate=3 * MB, - import_name="PIL", - ), - Dependency( - display_name="pillow-avif-plugin", - pypi_name="pillow-avif-plugin", - version="1.4.3", - size_estimate=11 * MB, - import_name="pillow_avif", - ), - Dependency( - display_name="FFMPEG", - pypi_name="ffmpeg-python", - version="0.2.0", - size_estimate=25 * KB, - import_name="ffmpeg", - ), - Dependency( - display_name="Requests", - pypi_name="requests", - version="2.28.2", - size_estimate=452 * KB, - ), - Dependency( - display_name="re2", - pypi_name="google-re2", - version="1.0", - size_estimate=275 * KB, - import_name="re2", - ), - Dependency( - display_name="scipy", - pypi_name="scipy", - version="1.9.3", - size_estimate=42 * MB, - ), - Dependency( - display_name="Wildcard Match", - pypi_name="wcmatch", - version="8.4.1", - size_estimate=39 * KB, - import_name="wcmatch", - ), - Dependency( - display_name="Numba", - pypi_name="numba", - version="0.57.1", - size_estimate=2.5 * MB, - ), - Dependency( - display_name="PyMatting", - pypi_name="PyMatting", - import_name="pymatting", - version="1.1.10", - size_estimate=52 * KB, - ), - Dependency( - display_name="ChaiNNer Extensions", - pypi_name="chainner_ext", - version="0.3.10", - size_estimate=2.0 * MB, - ), - ], -) - - -image_category = package.add_category( - name="Image", - description="Base image nodes.", - icon="BsFillImageFill", - color="#C53030", -) - -image_dimensions_category = package.add_category( - name="Image (Dimensions)", - description="Nodes that deal with changing the dimensions/resolution of images.", - icon="MdOutlinePhotoSizeSelectLarge", - color="#3182CE", -) - -image_adjustments_category = package.add_category( - name="Image (Adjustments)", - description="Nodes that deal with adjusting properties of images.", - icon="BsSliders", - color="#319795", -) - -image_filter_category = package.add_category( - name="Image (Filters)", - description="Nodes that deal with filtering images.", - icon="MdFilterAlt", - color="#38A169", -) - -image_utility_category = package.add_category( - name="Image (Utilities)", - description="Various utility nodes for images.", - icon="BsGear", - color="#00A3C4", -) - -image_channel_category = package.add_category( - name="Image (Channels)", - description="Nodes that deal with manipulating channels of images.", - icon="MdAllOut", - color="#D69E2E", -) - -material_textures_category = package.add_category( - name="Material Textures", - description="Modify and create material textures for games & 3D models.", - icon="GiRolledCloth", - color="#827DFB", -) - -utility_category = package.add_category( - name="Utility", - description="Various utility nodes.", - icon="BsGearFill", - color="#718096", -) - - -logger.debug(f"Loaded package {package.name}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/__init__.py deleted file mode 100644 index af48d2f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -from .. import image_category - -io_group = image_category.add_node_group("Input & Output") -batch_processing_group = image_category.add_node_group("Batch Processing") -video_frames_group = image_category.add_node_group("Video Frames") -create_images_group = image_category.add_node_group("Create Images") - -batch_processing_group.order = [ - "chainner:image:load_images", - "chainner:image:load_image_pairs", - "chainner:image:split_spritesheet", - "chainner:image:merge_spritesheet", -] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/load_image_pairs.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/load_image_pairs.py deleted file mode 100644 index 8dfdd3f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/load_image_pairs.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -import numpy as np - -from api import Iterator, IteratorOutputInfo -from nodes.groups import Condition, if_group -from nodes.impl.image_formats import get_available_image_formats -from nodes.properties.inputs import BoolInput, DirectoryInput, NumberInput -from nodes.properties.outputs import ( - DirectoryOutput, - ImageOutput, - NumberOutput, - TextOutput, -) -from nodes.utils.utils import list_all_files_sorted - -from .. import batch_processing_group -from ..io.load_image import load_image_node - - -@batch_processing_group.register( - schema_id="chainner:image:load_image_pairs", - name="Load Image Pairs", - description="Iterate over all files in two directories and run the provided nodes on the image files together. This can be useful for things like making comparisons of already processed content.", - icon="BsImages", - inputs=[ - DirectoryInput("Directory A"), - DirectoryInput("Directory B"), - BoolInput("Use limit", default=False), - if_group(Condition.bool(2, True))( - NumberInput("Limit", default=10, minimum=1).with_docs( - "Limit the number of images to iterate over. This can be useful for testing the iterator without having to iterate over all images." - ) - ), - BoolInput("Stop on first error", default=False).with_docs( - "Instead of collecting errors and throwing them at the end of processing, stop iteration and throw an error as soon as one occurs.", - hint=True, - ), - ], - outputs=[ - ImageOutput("Image A"), - ImageOutput("Image B"), - DirectoryOutput("Directory A", output_type="Input0"), - DirectoryOutput("Directory B", output_type="Input1"), - TextOutput("Subdirectory Path A"), - TextOutput("Subdirectory Path B"), - TextOutput("Image Name A"), - TextOutput("Image Name B"), - NumberOutput( - "Index", - output_type="if Input2 { min(uint, Input3 - 1) } else { uint }", - ).with_docs("A counter that starts at 0 and increments by 1 for each image."), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 1, 4, 5, 6, 7, 8]), - kind="newIterator", -) -def load_image_pairs_node( - directory_a: Path, - directory_b: Path, - use_limit: bool, - limit: int, - fail_fast: bool, -) -> tuple[ - Iterator[tuple[np.ndarray, np.ndarray, str, str, str, str, int]], Path, Path -]: - def load_images(filepaths: tuple[Path, Path], index: int): - path_a, path_b = filepaths - img_a, img_dir_a, basename_a = load_image_node(path_a) - img_b, img_dir_b, basename_b = load_image_node(path_b) - - # Get relative path from root directory passed by Iterator directory input - rel_path_a = os.path.relpath(img_dir_a, directory_a) - rel_path_b = os.path.relpath(img_dir_b, directory_b) - return img_a, img_b, rel_path_a, rel_path_b, basename_a, basename_b, index - - supported_filetypes = get_available_image_formats() - - image_files_a: list[Path] = list_all_files_sorted(directory_a, supported_filetypes) - image_files_b: list[Path] = list_all_files_sorted(directory_b, supported_filetypes) - - assert len(image_files_a) == len(image_files_b), ( - "Number of images in directories A and B must be equal. " - f"Directory A: {directory_a} has {len(image_files_a)} images. " - f"Directory B: {directory_b} has {len(image_files_b)} images." - ) - - if use_limit: - image_files_a = image_files_a[:limit] - image_files_b = image_files_b[:limit] - - image_files = list(zip(image_files_a, image_files_b)) - - return ( - Iterator.from_list(image_files, load_images, fail_fast), - directory_a, - directory_b, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/load_images.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/load_images.py deleted file mode 100644 index 4e0ee6c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/load_images.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import annotations - -import os -from pathlib import Path - -import numpy as np -from wcmatch import glob - -from api import Iterator, IteratorOutputInfo -from nodes.groups import Condition, if_group -from nodes.impl.image_formats import get_available_image_formats -from nodes.properties.inputs import BoolInput, DirectoryInput, NumberInput, TextInput -from nodes.properties.outputs import ( - DirectoryOutput, - ImageOutput, - NumberOutput, - TextOutput, -) -from nodes.utils.utils import alphanumeric_sort - -from .. import batch_processing_group -from ..io.load_image import load_image_node - - -def extension_filter(lst: list[str]) -> str: - """generates a mcmatch.glob expression to filter files with specific extensions - ex. {*,**/*}@(*.png|*.jpg|...)""" - return "**/*@(" + "|".join(lst) + ")" - - -def list_glob(directory: Path, globexpr: str, ext_filter: list[str]) -> list[Path]: - extension_expr = extension_filter(ext_filter) - - flags = ( - glob.EXTGLOB - | glob.BRACE - | glob.GLOBSTAR - | glob.NEGATE - | glob.DOTGLOB - | glob.NEGATEALL - ) - - foo = list(glob.iglob(globexpr, root_dir=directory, flags=flags)) - - filtered = glob.globfilter( - foo, - extension_expr, - flags=flags | glob.IGNORECASE, - ) - - return [ - Path(x) - for x in sorted( - {str(directory / f) for f in filtered}, - key=alphanumeric_sort, - ) - ] - - -@batch_processing_group.register( - schema_id="chainner:image:load_images", - name="Load Images", - description=[ - "Iterate over all files in a directory/folder (batch processing) and run the provided nodes on just the image files. Supports the same file types as `chainner:image:load`.", - "Optionally, you can toggle whether to iterate recursively (subdirectories) or use a glob expression to filter the files.", - ], - icon="BsImages", - inputs=[ - DirectoryInput(), - BoolInput("Use WCMatch glob expression", default=False), - if_group(Condition.bool(1, False))( - BoolInput("Recursive").with_docs("Iterate recursively over subdirectories.") - ), - if_group(Condition.bool(1, True))( - TextInput("WCMatch Glob expression", default="**/*").with_docs( - "For information on how to use WCMatch glob expressions, see [here](https://facelessuser.github.io/wcmatch/glob/)." - ), - ), - BoolInput("Use limit", default=False), - if_group(Condition.bool(4, True))( - NumberInput("Limit", default=10, minimum=1).with_docs( - "Limit the number of images to iterate over. This can be useful for testing the iterator without having to iterate over all images." - ) - ), - BoolInput("Stop on first error", default=False).with_docs( - "Instead of collecting errors and throwing them at the end of processing, stop iteration and throw an error as soon as one occurs.", - hint=True, - ), - ], - outputs=[ - ImageOutput(), - DirectoryOutput("Directory", output_type="Input0"), - TextOutput("Subdirectory Path"), - TextOutput("Name"), - NumberOutput( - "Index", - output_type="if Input4 { min(uint, Input5 - 1) } else { uint }", - ), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 2, 3, 4]), - kind="newIterator", -) -def load_images_node( - directory: Path, - use_glob: bool, - is_recursive: bool, - glob_str: str, - use_limit: bool, - limit: int, - fail_fast: bool, -) -> tuple[Iterator[tuple[np.ndarray, str, str, int]], Path]: - def load_image(path: Path, index: int): - img, img_dir, basename = load_image_node(path) - # Get relative path from root directory passed by Iterator directory input - rel_path = os.path.relpath(img_dir, directory) - return img, rel_path, basename, index - - supported_filetypes = get_available_image_formats() - - if not use_glob: - glob_str = "**/*" if is_recursive else "*" - - just_image_files = list_glob(directory, glob_str, supported_filetypes) - if not len(just_image_files): - raise FileNotFoundError(f"{directory} has no valid images.") - - if use_limit: - just_image_files = just_image_files[:limit] - - return Iterator.from_list(just_image_files, load_image, fail_fast), directory diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/merge_spritesheet.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/merge_spritesheet.py deleted file mode 100644 index f9d9a96..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/merge_spritesheet.py +++ /dev/null @@ -1,68 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from api import Collector, IteratorInputInfo -from nodes.properties.inputs import ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import batch_processing_group - - -@batch_processing_group.register( - schema_id="chainner:image:merge_spritesheet", - name="Merge Spritesheet", - description=[ - "Combines an iterable sequence into a single image spritesheet.", - ], - icon="BsGrid3X3", - inputs=[ - ImageInput("Image Sequence"), - NumberInput( - "Number of rows (height)", - controls_step=1, - minimum=1, - default=1, - ).with_docs( - "The number of rows to split the image into. The height of the image must be a multiple of this number." - ), - NumberInput( - "Number of columns (width)", - controls_step=1, - minimum=1, - default=1, - ).with_docs( - "The number of columns to split the image into. The width of the image must be a multiple of this number." - ), - ], - iterator_inputs=IteratorInputInfo(inputs=0), - outputs=[ - ImageOutput( - image_type=""" - Image { - height: max(Input0.height * Input1, 1), - width: max(Input0.width * Input2, 1), - channels: Input0.channels, - }""" - ) - ], - kind="collector", -) -def merge_spritesheet_node( - _: None, - rows: int, - columns: int, -) -> Collector[np.ndarray, np.ndarray]: - results = [] - - def on_iterate(tile: np.ndarray): - results.append(tile) - - def on_complete(): - result_rows = [] - for i in range(rows): - row = np.concatenate(results[i * columns : (i + 1) * columns], axis=1) - result_rows.append(row) - return np.concatenate(result_rows, axis=0) - - return Collector(on_iterate=on_iterate, on_complete=on_complete) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/split_spritesheet.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/split_spritesheet.py deleted file mode 100644 index 366afaf..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/batch_processing/split_spritesheet.py +++ /dev/null @@ -1,84 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from api import Iterator, IteratorOutputInfo -from nodes.properties.inputs import ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput, NumberOutput -from nodes.utils.utils import get_h_w_c - -from .. import batch_processing_group - - -@batch_processing_group.register( - schema_id="chainner:image:split_spritesheet", - name="Split Spritesheet", - description=[ - "Iterate over sub-images in a single image spritesheet.", - "This iterator splits the image into an iterable sequence of tiles.", - ], - icon="BsFillGrid3X3GapFill", - inputs=[ - ImageInput("Spritesheet"), - NumberInput( - "Number of rows (height)", - controls_step=1, - minimum=1, - default=1, - ).with_docs( - "The number of rows to split the image into. The height of the image must be a multiple of this number." - ), - NumberInput( - "Number of columns (width)", - controls_step=1, - minimum=1, - default=1, - ).with_docs( - "The number of columns to split the image into. The width of the image must be a multiple of this number." - ), - ], - outputs=[ - ImageOutput( - image_type=""" - Image { - height: max(floor(Input0.height / Input1), 1), - width: max(floor(Input0.width / Input2), 1), - channels: Input0.channels, - }""" - ), - NumberOutput("Index", output_type="min(uint, Input1 * Input2 - 1)").with_docs( - "A counter that starts at 0 and increments by 1 for each image." - ), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 1], length_type="Input1 * Input2"), - kind="newIterator", -) -def split_spritesheet_node( - sprite_sheet: np.ndarray, - rows: int, - columns: int, -) -> Iterator[tuple[np.ndarray, int]]: - h, w, _ = get_h_w_c(sprite_sheet) - assert ( - h % rows == 0 - ), "Height of sprite sheet must be a multiple of the number of rows" - assert ( - w % columns == 0 - ), "Width of sprite sheet must be a multiple of the number of columns" - - individual_h = h // rows - individual_w = w // columns - - def get_sprite(index: int): - row = index // columns - col = index % columns - - sprite = sprite_sheet[ - row * individual_h : (row + 1) * individual_h, - col * individual_w : (col + 1) * individual_w, - ] - - return sprite, index - - # We just need the index, so we can pass in a list of None's - return Iterator.from_range(rows * columns, get_sprite) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_checkerboard.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_checkerboard.py deleted file mode 100644 index 2b17d0b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_checkerboard.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.impl.color.color import Color -from nodes.impl.image_utils import as_target_channels -from nodes.properties.inputs import ColorInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import create_images_group - - -@create_images_group.register( - schema_id="chainner:image:create_checkerboard", - name="Create Checkerboard", - description="Create a Checkerboard of specified dimensions filled with the given colors for the squares.", - icon="MdBorderAll", - inputs=[ - NumberInput("Width", minimum=1, unit="px", default=1024), - NumberInput("Height", minimum=1, unit="px", default=1024), - ColorInput( - "Color 1", channels=[1, 3, 4], default=Color.bgr((0.75, 0.75, 0.75)) - ).with_id(2), - ColorInput( - "Color 2", channels=[1, 3, 4], default=Color.bgr((0.35, 0.35, 0.35)) - ).with_id(3), - NumberInput("Square Size", minimum=1, default=32), - ], - outputs=[ - ImageOutput( - image_type=""" - Image { - width: Input0, - height: Input1, - channels: max(Input2.channels, Input3.channels), - }""", - ) - ], -) -def create_checkerboard_node( - width: int, - height: int, - color_1: Color, - color_2: Color, - square_size: int, -) -> np.ndarray: - # Determine the number of squares in each direction - num_cols = (width + square_size - 1) // square_size - num_rows = (height + square_size - 1) // square_size - - max_channels = max(color_1.channels, color_2.channels) - - color_a = Color.from_1x1_image( - as_target_channels(color_1.to_1x1_image(), max_channels) - ) - color_b = Color.from_1x1_image( - as_target_channels(color_2.to_1x1_image(), max_channels) - ) - - img = np.zeros( - (height, width, max_channels), dtype=np.float32 - ) # Create a new buffer with all zeros - - # Fill the checkerboard with alternating squares - for i in range(num_rows): - for j in range(num_cols): - if (i + j) % 2 == 0: - img[ - max(0, height - (i + 1) * square_size) : max( - 0, height - i * square_size - ), - j * square_size : min(width, (j + 1) * square_size), - ] = color_a.value - else: - img[ - max(0, height - (i + 1) * square_size) : max( - 0, height - i * square_size - ), - j * square_size : min(width, (j + 1) * square_size), - ] = color_b.value - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_color.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_color.py deleted file mode 100644 index 5498ca8..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_color.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.color.color import Color -from nodes.properties.inputs import ColorInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import create_images_group - - -@create_images_group.register( - schema_id="chainner:image:create_color", - name="Create Color", - description="Create an image of specified dimensions filled with the given color.", - icon="MdFormatColorFill", - inputs=[ - ColorInput(), - NumberInput("Width", minimum=1, unit="px", default=1), - NumberInput("Height", minimum=1, unit="px", default=1), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input1", - height="Input2", - channels="Input0.channels", - ), - assume_normalized=True, - ) - ], -) -def create_color_node(color: Color, width: int, height: int) -> np.ndarray: - return color.to_image(width, height) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_colorwheel.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_colorwheel.py deleted file mode 100644 index 95f8f93..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_colorwheel.py +++ /dev/null @@ -1,53 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -import navi -from nodes.impl.gradients import ( - conic_gradient, -) -from nodes.properties.inputs import ( - NumberInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import create_images_group - - -@create_images_group.register( - schema_id="chainner:image:create_colorwheel", - name="Create Colorwheel", - description="Create an image with a color wheel.", - icon="MdLens", - inputs=[ - NumberInput("Size", minimum=1, unit="px", default=512), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( # Creates a square output buffer from scratch and sets it to the width and height defined by the (Number)Input0 defined earlier - width="Input0", - height="Input0", - ), - channels=3, - ) - ], -) -def create_colorwheel_node( - size: int, -) -> np.ndarray: - img = np.zeros((size, size), dtype=np.float32) # Create a new buffer with all zeros - - conic_gradient( - img, rotation=0 * np.pi / 180 - ) # Create our hue component with a chaiNNer conic gradient - - w = np.ones( - (size, size), dtype=np.float32 - ) # Create a new buffer with a value of "one" - - hsv = np.stack((img, w, w), axis=2) # Stack our HSV channels to BGR order - - hsv[:, :, 0] *= 360 # Rotate the channel order - - return cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_gradient.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_gradient.py deleted file mode 100644 index d3e1840..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_gradient.py +++ /dev/null @@ -1,152 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -import navi -from nodes.groups import if_enum_group -from nodes.impl.color.color import Color -from nodes.impl.gradients import ( - conic_gradient, - diagonal_gradient, - horizontal_gradient, - radial_gradient, - vertical_gradient, -) -from nodes.impl.image_utils import as_target_channels -from nodes.properties.inputs import ( - BoolInput, - ColorInput, - EnumInput, - NumberInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import create_images_group - - -class GradientStyle(Enum): - HORIZONTAL = "Horizontal" - VERTICAL = "Vertical" - DIAGONAL = "Diagonal" - RADIAL = "Radial" - CONIC = "Conic" - - -@create_images_group.register( - schema_id="chainner:image:create_gradient", - name="Create Gradient", - description="Create an image with a gradient.", - icon="MdFormatColorFill", - inputs=[ - NumberInput("Width", minimum=1, unit="px", default=64), - NumberInput("Height", minimum=1, unit="px", default=64), - ColorInput("Color 1", default=Color.gray(0)).with_id(9), - ColorInput("Color 2", default=Color.gray(1)).with_id(10), - BoolInput("Reverse", default=False).with_id(2), - EnumInput(GradientStyle, default=GradientStyle.HORIZONTAL).with_id(3), - if_enum_group(3, GradientStyle.DIAGONAL)( - SliderInput( - "Angle", - minimum=0, - maximum=360, - default=45, - unit="deg", - ).with_id(4), - NumberInput( - "Width", - minimum=0, - default=100, - unit="px", - ).with_id(5), - ), - if_enum_group(3, GradientStyle.RADIAL)( - SliderInput( - "Inner Radius", - minimum=0, - maximum=100, - default=0, - unit="%", - ).with_id(6), - SliderInput( - "Outer Radius", - minimum=0, - maximum=100, - default=100, - unit="%", - ).with_id(7), - ), - if_enum_group(3, GradientStyle.CONIC)( - SliderInput( - "Rotation", - minimum=0, - maximum=360, - default=0, - unit="deg", - ).with_id(8), - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input0", - height="Input1", - channels="max(Input9.channels, Input10.channels)", - ) - ) - ], -) -def create_gradient_node( - width: int, - height: int, - color_1: Color, - color_2: Color, - reverse: bool, - gradient_style: GradientStyle, - diagonal_angle: int, - diagonal_width: int, - inner_radius_percent: int, - outer_radius_percent: int, - conic_rotation: int, -) -> np.ndarray: - img = np.zeros((height, width), dtype=np.float32) - - if gradient_style == GradientStyle.HORIZONTAL: - horizontal_gradient(img) - - elif gradient_style == GradientStyle.VERTICAL: - vertical_gradient(img) - - elif gradient_style == GradientStyle.DIAGONAL: - diagonal_gradient(img, diagonal_angle * np.pi / 180, diagonal_width) - - elif gradient_style == GradientStyle.RADIAL: - radial_gradient( - img, - inner_radius_percent=inner_radius_percent / 100, - outer_radius_percent=outer_radius_percent / 100, - ) - - elif gradient_style == GradientStyle.CONIC: - conic_gradient(img, rotation=conic_rotation * np.pi / 180) - - if reverse: - color_1, color_2 = color_2, color_1 - - if color_1.channels > color_2.channels: - color_2 = Color.from_1x1_image( - as_target_channels(color_2.to_1x1_image(), color_1.channels) - ) - elif color_2.channels > color_1.channels: - color_1 = Color.from_1x1_image( - as_target_channels(color_1.to_1x1_image(), color_2.channels) - ) - - c1 = color_1.to_image(width=width, height=height) - c2 = color_2.to_image(width=width, height=height) - if color_1.channels > 1: - img = np.dstack((img,) * color_1.channels) - - return c2 * img + c1 * (1 - img) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_noise.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_noise.py deleted file mode 100644 index db73d8c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/create_noise.py +++ /dev/null @@ -1,213 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from typing import Callable - -import numpy as np - -import navi -from nodes.groups import if_enum_group, seed_group -from nodes.impl.image_utils import cartesian_product -from nodes.impl.noise_functions.blue import create_blue_noise -from nodes.impl.noise_functions.noise_generator import NoiseGenerator -from nodes.impl.noise_functions.simplex import SimplexNoise -from nodes.impl.noise_functions.value import ValueNoise -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - NumberInput, - SeedInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.seed import Seed - -from .. import create_images_group - - -class NoiseMethod(Enum): - VALUE_NOISE = "Value Noise" - SMOOTH_VALUE_NOISE = "Smooth Value Noise" - SIMPLEX = "Simplex" - BLUE_NOISE = "Blue Noise" - - -class FractalMethod(Enum): - NONE = "None" - PINK_NOISE = "Pink noise" - - -def _generate_noise( - generator: Callable[[int], NoiseGenerator], - width: int, - height: int, - scale: float, - brightness: float, - tile_horizontal: bool = False, - tile_vertical: bool = False, - tile_spherical: bool = False, -): - h, w = height, width - pixels = cartesian_product([np.arange(h), np.arange(w)]) - points = np.array(pixels) - if tile_spherical: - tile_horizontal = False - tile_vertical = False - if tile_horizontal: - x = points[:, 1] * 2 * np.pi / w - cx = (w * np.cos(x) / np.pi / 2).reshape((-1, 1)) - sx = (w * np.sin(x) / np.pi / 2).reshape((-1, 1)) - points = np.concatenate([points[:, :1], cx, sx], axis=1) - if tile_vertical: - x = points[:, 0] * 2 * np.pi / h - cx = (h * np.cos(x) / np.pi / 2).reshape((-1, 1)) - sx = (h * np.sin(x) / np.pi / 2).reshape((-1, 1)) - points = np.concatenate([points[:, 1:], cx, sx], axis=1) - if tile_spherical: - theta = points[:, 0] * np.pi / h - alpha = points[:, 1] * 2 * np.pi / w - - y = w * np.cos(theta).reshape((-1, 1)) / np.pi / 2 - r = h * np.sin(theta).reshape((-1, 1)) - - x = r * np.cos(alpha).reshape((-1, 1)) / np.pi / 2 - z = r * np.sin(alpha).reshape((-1, 1)) / np.pi / 2 - - points = np.concatenate([x, y, z], axis=1) - - output = generator(points.shape[1]).evaluate(points / scale) - - return output.reshape((h, w)) * brightness - - -@create_images_group.register( - schema_id="chainner:image:create_noise", - name="Create Noise", - description="Create an image of specified dimensions filled with one of a variety of noises.", - icon="MdFormatColorFill", - inputs=[ - NumberInput("Width", minimum=1, unit="px", default=256), - NumberInput("Height", minimum=1, unit="px", default=256), - seed_group(SeedInput()), - EnumInput( - NoiseMethod, - default=NoiseMethod.SIMPLEX, - option_labels={NoiseMethod.SMOOTH_VALUE_NOISE: "Value Noise (smooth)"}, - ).with_id(3), - if_enum_group( - 3, - ( - NoiseMethod.SIMPLEX, - NoiseMethod.VALUE_NOISE, - NoiseMethod.SMOOTH_VALUE_NOISE, - ), - )( - NumberInput("Scale", minimum=1, default=50, precision=1).with_id(4), - SliderInput( - "Brightness", minimum=0, default=100, maximum=100, precision=2 - ).with_id(5), - BoolInput("Tile Horizontal", default=False).with_id(10), - BoolInput("Tile Vertical", default=False).with_id(11), - BoolInput("Tile Spherical", default=False).with_id(12), - EnumInput(FractalMethod, default=FractalMethod.NONE).with_id(6), - if_enum_group(6, FractalMethod.PINK_NOISE)( - NumberInput( - "Layers", minimum=2, maximum=20, default=3, precision=0 - ).with_id(7), - NumberInput("Scale Ratio", minimum=1, default=2, precision=2).with_id( - 8 - ), - NumberInput( - "Brightness Ratio", minimum=1, default=2, precision=2 - ).with_id(9), - BoolInput("Increment Seed", default=True).with_id(13), - ), - ), - if_enum_group(3, NoiseMethod.BLUE_NOISE)( - SliderInput( - "Standard Deviation", - minimum=1, - maximum=100, - default=1.5, - precision=3, - scale="log-offset", - ).with_id(14) - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input0", - height="Input1", - ), - channels=1, - ) - ], -) -def create_noise_node( - width: int, - height: int, - seed_obj: Seed, - noise_method: NoiseMethod, - scale: float, - brightness: float, - tile_horizontal: bool, - tile_vertical: bool, - tile_spherical: bool, - fractal_method: FractalMethod, - layers: int, - scale_ratio: float, - brightness_ratio: float, - increment_seed: bool, - standard_deviation: float, -) -> np.ndarray: - brightness /= 100 - seed = seed_obj.to_u32() - - if noise_method == NoiseMethod.BLUE_NOISE: - return create_blue_noise( - (height, width), - standard_deviation=standard_deviation, - seed=seed_obj.to_u32(), - ).astype(np.float32) / (width * height - 1) - - generator_class: Callable[[int, int], NoiseGenerator] - if noise_method == NoiseMethod.SIMPLEX: - generator_class = SimplexNoise - elif noise_method == NoiseMethod.VALUE_NOISE: - generator_class = lambda dim, seed: ValueNoise(dim, seed, smooth=False) # noqa: E731 - elif noise_method == NoiseMethod.SMOOTH_VALUE_NOISE: - generator_class = lambda dim, seed: ValueNoise(dim, seed, smooth=True) # noqa: E731 - - if fractal_method == FractalMethod.NONE: - return _generate_noise( - lambda dim: generator_class(dim, seed), - width=width, - height=height, - scale=scale, - brightness=brightness, - tile_horizontal=tile_horizontal, - tile_vertical=tile_vertical, - tile_spherical=tile_spherical, - ) - - if fractal_method == FractalMethod.PINK_NOISE: - img = np.zeros((height, width), dtype=np.float32) - total_brightness = 0 - for i in range(layers): - rel_brightness = 1 / (brightness_ratio**i) - total_brightness += rel_brightness - img += _generate_noise( - lambda dim: generator_class(dim, seed), # noqa: B023 - width=width, - height=height, - scale=scale / (scale_ratio**i), - brightness=brightness * rel_brightness, - tile_horizontal=tile_horizontal, - tile_vertical=tile_vertical, - tile_spherical=tile_spherical, - ) - if increment_seed: - seed += 1 - img /= total_brightness - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/text_as_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/text_as_image.py deleted file mode 100644 index 5dadc92..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/create_images/text_as_image.py +++ /dev/null @@ -1,214 +0,0 @@ -from __future__ import annotations - -import os -import sys -from enum import Enum - -import numpy as np -from PIL import Image, ImageDraw, ImageFont - -from nodes.groups import icon_set_group, menu_icon_row_group -from nodes.impl.caption import get_font_size -from nodes.impl.color.color import Color -from nodes.impl.image_utils import normalize, to_uint8 -from nodes.properties.inputs import ( - BoolInput, - ColorInput, - EnumInput, - NumberInput, - TextInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import create_images_group - -TEXT_AS_IMAGE_FONT_PATH = [ - [ - "Roboto/Roboto-Regular.ttf", - "Roboto/Roboto-Italic.ttf", - ], - ["Roboto/Roboto-Bold.ttf", "Roboto/Roboto-BoldItalic.ttf"], -] - - -class TextAsImageAlignment(Enum): - LEFT = "left" - CENTER = "center" - RIGHT = "right" - - -class TextAsImagePosition(Enum): - TOP_LEFT = "top_left" - TOP_CENTERED = "top_centered" - TOP_RIGHT = "top_right" - CENTERED_LEFT = "centered_left" - CENTERED = "centered" - CENTERED_RIGHT = "centered_right" - BOTTOM_LEFT = "bottom_left" - BOTTOM_CENTERED = "bottom_centered" - BOTTOM_RIGHT = "bottom_right" - - -TEXT_AS_IMAGE_POSITION_LABELS = { - TextAsImagePosition.TOP_LEFT: "Top left", - TextAsImagePosition.TOP_CENTERED: "Top centered", - TextAsImagePosition.TOP_RIGHT: "Top right", - TextAsImagePosition.CENTERED_LEFT: "Centered left", - TextAsImagePosition.CENTERED: "Centered", - TextAsImagePosition.CENTERED_RIGHT: "Centered right", - TextAsImagePosition.BOTTOM_LEFT: "Bottom left", - TextAsImagePosition.BOTTOM_CENTERED: "Bottom centered", - TextAsImagePosition.BOTTOM_RIGHT: "Bottom right", -} - -TEXT_AS_IMAGE_X_Y_REF_FACTORS = { - TextAsImagePosition.TOP_LEFT: {"x": np.array([0, 0.5]), "y": np.array([0, 0.5])}, - TextAsImagePosition.TOP_CENTERED: { - "x": np.array([0.5, 0]), - "y": np.array([0, 0.5]), - }, - TextAsImagePosition.TOP_RIGHT: {"x": np.array([1, -0.5]), "y": np.array([0, 0.5])}, - TextAsImagePosition.CENTERED_LEFT: { - "x": np.array([0, 0.5]), - "y": np.array([0.5, 0]), - }, - TextAsImagePosition.CENTERED: {"x": np.array([0.5, 0]), "y": np.array([0.5, 0])}, - TextAsImagePosition.CENTERED_RIGHT: { - "x": np.array([1, -0.5]), - "y": np.array([0.5, 0]), - }, - TextAsImagePosition.BOTTOM_LEFT: { - "x": np.array([0, 0.5]), - "y": np.array([1, -0.5]), - }, - TextAsImagePosition.BOTTOM_CENTERED: { - "x": np.array([0.5, 0]), - "y": np.array([1, -0.5]), - }, - TextAsImagePosition.BOTTOM_RIGHT: { - "x": np.array([1, -0.5]), - "y": np.array([1, -0.5]), - }, -} - - -@create_images_group.register( - schema_id="chainner:image:text_as_image", - name="Text As Image", - description="Create an image using any text.", - icon="MdTextFields", - inputs=[ - TextInput("Text", multiline=True, label_style="hidden"), - menu_icon_row_group()( - icon_set_group("Style")( - BoolInput("Bold", default=False, icon="FaBold").with_id(1), - BoolInput("Italic", default=False, icon="FaItalic").with_id(2), - ), - EnumInput( - TextAsImageAlignment, - label="Alignment", - preferred_style="icons", - icons={ - TextAsImageAlignment.LEFT: "FaAlignLeft", - TextAsImageAlignment.CENTER: "FaAlignCenter", - TextAsImageAlignment.RIGHT: "FaAlignRight", - }, - default=TextAsImageAlignment.CENTER, - ).with_id(4), - ), - ColorInput(channels=[3], default=Color.bgr((0, 0, 0))).with_id(3), - NumberInput( - "Width", - minimum=1, - maximum=None, - controls_step=1, - precision=0, - default=500, - ).with_id(5), - NumberInput( - "Height", - minimum=1, - maximum=None, - controls_step=1, - precision=0, - default=100, - ).with_id(6), - EnumInput( - TextAsImagePosition, - label="Position", - option_labels=TEXT_AS_IMAGE_POSITION_LABELS, - default=TextAsImagePosition.CENTERED, - ).with_id(7), - ], - outputs=[ - ImageOutput( - image_type=""" - Image { - width: Input5, - height: Input6, - } - """, - channels=4, - assume_normalized=True, - ) - ], -) -def text_as_image_node( - text: str, - bold: bool, - italic: bool, - alignment: TextAsImageAlignment, - color: Color, - width: int, - height: int, - position: TextAsImagePosition, -) -> np.ndarray: - path = TEXT_AS_IMAGE_FONT_PATH[int(bold)][int(italic)] - font_path = os.path.join( - os.path.dirname(sys.modules["__main__"].__file__), # type: ignore - f"fonts/{path}", # type: ignore - ) - - lines = text.split("\n") - line_count, max_line = len(lines), max(lines, key=len) - - # Use a text as reference to get max size - font = ImageFont.truetype(font_path, size=100) - w_ref, h_ref = get_font_size(font, max_line)[0], get_font_size(font, "[§]")[1] - - # Calculate font size to fill the specified image size - w = int(width * 100.0 / w_ref) - h = int(height * 100.0 / (h_ref * line_count)) - font_size = min(w, h) - font = ImageFont.truetype(font_path, size=font_size) - w_text, h_text = get_font_size(font, max_line) - h_text *= line_count - - # Text color - ink = tuple(to_uint8(np.array(color.value))) - - # Create a PIL image to add text - pil_image = Image.new("RGBA", (width, height)) - drawing = ImageDraw.Draw(pil_image) - - x_ref = round( - np.sum(np.array([width, w_text]) * TEXT_AS_IMAGE_X_Y_REF_FACTORS[position]["x"]) # type: ignore - ) - y_ref = round( - np.sum( - np.array([height, h_text]) * TEXT_AS_IMAGE_X_Y_REF_FACTORS[position]["y"] # type: ignore - ) - ) - - drawing.text( - (x_ref, y_ref), - text, - font=font, - anchor="mm", - align=alignment.value, - fill=ink, # type: ignore - ) - - img = normalize(np.array(pil_image)) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/load_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/load_image.py deleted file mode 100644 index 7180506..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/load_image.py +++ /dev/null @@ -1,190 +0,0 @@ -from __future__ import annotations - -import os -import platform -from pathlib import Path -from typing import Callable, Iterable, Union - -import cv2 -import numpy as np -import pillow_avif # noqa: F401 -from PIL import Image -from sanic.log import logger - -from nodes.impl.dds.texconv import dds_to_png_texconv -from nodes.impl.image_formats import ( - get_available_image_formats, - get_opencv_formats, - get_pil_formats, -) -from nodes.properties.inputs import ImageFileInput -from nodes.properties.outputs import DirectoryOutput, FileNameOutput, LargeImageOutput -from nodes.utils.utils import get_h_w_c, split_file_path - -from .. import io_group - -_Decoder = Callable[[Path], Union[np.ndarray, None]] -""" -An image decoder. - -Of the given image is naturally not supported, the decoder may return `None` -instead of raising an exception. E.g. when the file extension indicates an -unsupported format. -""" - - -def get_ext(path: Path | str) -> str: - return split_file_path(path)[2].lower() - - -def remove_unnecessary_alpha(img: np.ndarray) -> np.ndarray: - """ - Removes the alpha channel from an image if it is not used. - """ - if get_h_w_c(img)[2] != 4: - return img - - unnecessary = ( - (img.dtype == np.uint8 and np.all(img[:, :, 3] == 255)) - or (img.dtype == np.uint16 and np.all(img[:, :, 3] == 65536)) - or (img.dtype == np.float32 and np.all(img[:, :, 3] == 1.0)) - or (img.dtype == np.float64 and np.all(img[:, :, 3] == 1.0)) - ) - - if unnecessary: - return img[:, :, :3] - return img - - -def _read_cv(path: Path) -> np.ndarray | None: - if get_ext(path) not in get_opencv_formats(): - # not supported - return None - - img = None - try: - img = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_UNCHANGED) - except Exception as cv_err: - logger.warning(f"Error loading image, trying with imdecode: {cv_err}") - - if img is None: - try: - img = cv2.imread(str(path), cv2.IMREAD_UNCHANGED) - except Exception as e: - raise RuntimeError( - f'Error reading image image from path "{path}". Image may be corrupt.' - ) from e - - if img is None: # type: ignore - raise RuntimeError( - f'Error reading image image from path "{path}". Image may be corrupt.' - ) - - return img - - -def _read_pil(path: Path) -> np.ndarray | None: - if get_ext(path) not in get_pil_formats(): - # not supported - return None - - im = Image.open(path) - img = np.array(im) - _, _, c = get_h_w_c(img) - if c == 3: - img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) - elif c == 4: - img = cv2.cvtColor(img, cv2.COLOR_RGBA2BGRA) - return img - - -def _read_dds(path: Path) -> np.ndarray | None: - if get_ext(path) != ".dds": - # not supported - return None - - if platform.system() != "Windows": - # texconv is only supported on Windows. - return None - - png = dds_to_png_texconv(path) - try: - img = _read_cv(png) - if img is not None: - img = remove_unnecessary_alpha(img) - return img - finally: - os.remove(png) - - -def _for_ext(ext: str | Iterable[str], decoder: _Decoder) -> _Decoder: - ext_set: set[str] = set() - if isinstance(ext, str): - ext_set.add(ext) - else: - ext_set.update(ext) - - return lambda path: decoder(path) if get_ext(path) in ext_set else None - - -_decoders: list[tuple[str, _Decoder]] = [ - ("pil-jpeg", _for_ext([".jpg", ".jpeg"], _read_pil)), - ("cv", _read_cv), - ("texconv-dds", _read_dds), - ("pil", _read_pil), -] - -valid_formats = get_available_image_formats() - - -@io_group.register( - schema_id="chainner:image:load", - name="Load Image", - description=( - "Load image from specified file. This node will output the loaded image, the" - " directory of the image file, and the name of the image file (without file" - " extension)." - ), - icon="BsFillImageFill", - inputs=[ - ImageFileInput(primary_input=True).with_docs( - "Select the path of an image file." - ) - ], - outputs=[ - LargeImageOutput() - .with_docs( - "The node will display a preview of the selected image as well as type" - " information for it. Connect this output to the input of another node to" - " pass the image to it." - ) - .suggest(), - DirectoryOutput("Directory", of_input=0), - FileNameOutput("Name", of_input=0), - ], -) -def load_image_node(path: Path) -> tuple[np.ndarray, Path, str]: - logger.debug(f"Reading image from path: {path}") - - dirname, basename, _ = split_file_path(path) - - img = None - error = None - for name, decoder in _decoders: - try: - img = decoder(Path(path)) - except Exception as e: - error = e - logger.warning(f"Decoder {name} failed") - - if img is not None: - break - - if img is None: - if error is not None: - raise error - raise RuntimeError( - f'The image "{path}" you are trying to read cannot be read by chaiNNer.' - ) - - return img, dirname, basename diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/save_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/save_image.py deleted file mode 100644 index ebdd4f1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/save_image.py +++ /dev/null @@ -1,430 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from pathlib import Path -from typing import Literal - -import cv2 -import numpy as np -import pillow_avif # noqa: F401 -from PIL import Image -from sanic.log import logger - -from api import KeyInfo, Lazy -from nodes.groups import Condition, if_enum_group, if_group -from nodes.impl.dds.format import ( - BC7_FORMATS, - BC123_FORMATS, - LEGACY_TO_DXGI, - PREFER_DX9, - WITH_ALPHA, - DDSFormat, - to_dxgi, -) -from nodes.impl.dds.texconv import save_as_dds -from nodes.impl.image_utils import cv_save_image, to_uint8, to_uint16 -from nodes.properties.inputs import ( - BoolInput, - DirectoryInput, - DropDownGroup, - DropDownInput, - EnumInput, - ImageInput, - RelativePathInput, - SliderInput, -) -from nodes.utils.utils import get_h_w_c - -from .. import io_group - - -class ImageFormat(Enum): - PNG = "png" - JPG = "jpg" - GIF = "gif" - BMP = "bmp" - TIFF = "tiff" - WEBP = "webp" - TGA = "tga" - DDS = "dds" - AVIF = "avif" - - @property - def extension(self) -> str: - return self.value - - -IMAGE_FORMAT_LABELS: dict[ImageFormat, str] = { - ImageFormat.PNG: "PNG", - ImageFormat.JPG: "JPG", - ImageFormat.GIF: "GIF", - ImageFormat.BMP: "BMP", - ImageFormat.TIFF: "TIFF", - ImageFormat.WEBP: "WEBP", - ImageFormat.TGA: "TGA", - ImageFormat.DDS: "DDS", - ImageFormat.AVIF: "AVIF", -} - - -class JpegSubsampling(Enum): - FACTOR_444 = int(cv2.IMWRITE_JPEG_SAMPLING_FACTOR_444) - FACTOR_440 = int(cv2.IMWRITE_JPEG_SAMPLING_FACTOR_440) - FACTOR_422 = int(cv2.IMWRITE_JPEG_SAMPLING_FACTOR_422) - FACTOR_420 = int(cv2.IMWRITE_JPEG_SAMPLING_FACTOR_420) - - -class AvifSubsampling(Enum): - FACTOR_444 = "4:4:4" - FACTOR_422 = "4:2:2" - FACTOR_420 = "4:2:0" - FACTOR_400 = "4:0:0" - - -class PngColorDepth(Enum): - U8 = "u8" - U16 = "u16" - - -class TiffColorDepth(Enum): - U8 = "u8" - U16 = "u16" - F32 = "f32" - - -SUPPORTED_DDS_FORMATS: list[tuple[DDSFormat, str]] = [ - ("BC1_UNORM_SRGB", "BC1 (4bpp, sRGB, 1-bit Alpha)"), - ("BC1_UNORM", "BC1 (4bpp, Linear, 1-bit Alpha)"), - ("BC3_UNORM_SRGB", "BC3 (8bpp, sRGB, 8-bit Alpha)"), - ("BC3_UNORM", "BC3 (8bpp, Linear, 8-bit Alpha)"), - ("BC4_UNORM", "BC4 (4bpp, Grayscale)"), - ("BC5_UNORM", "BC5 (8bpp, Unsigned, 2-channel normal)"), - ("BC5_SNORM", "BC5 (8bpp, Signed, 2-channel normal)"), - ("BC7_UNORM_SRGB", "BC7 (8bpp, sRGB, 8-bit Alpha)"), - ("BC7_UNORM", "BC7 (8bpp, Linear, 8-bit Alpha)"), - ("DXT1", "DXT1 (4bpp, Linear, 1-bit Alpha)"), - ("DXT3", "DXT3 (8bpp, Linear, 4-bit Alpha)"), - ("DXT5", "DXT5 (8bpp, Linear, 8-bit Alpha)"), - ("R8G8B8A8_UNORM_SRGB", "RGBA (32bpp, sRGB, 8-bit Alpha)"), - ("R8G8B8A8_UNORM", "RGBA (32bpp, Linear, 8-bit Alpha)"), - ("B8G8R8A8_UNORM_SRGB", "BGRA (32bpp, sRGB, 8-bit Alpha)"), - ("B8G8R8A8_UNORM", "BGRA (32bpp, Linear, 8-bit Alpha)"), - ("B5G5R5A1_UNORM", "BGRA (16bpp, Linear, 1-bit Alpha)"), - ("B5G6R5_UNORM", "BGR (16bpp, Linear)"), - ("B8G8R8X8_UNORM_SRGB", "BGRX (32bpp, sRGB)"), - ("B8G8R8X8_UNORM", "BGRX (32bpp, Linear)"), - ("R8G8_UNORM", "RG (16bpp, Linear)"), - ("R8_UNORM", "R (8bpp, Linear)"), -] - - -def DdsFormatDropdown() -> DropDownInput: - return DropDownInput( - input_type="DdsFormat", - label="DDS Format", - options=[{"option": title, "value": f} for f, title in SUPPORTED_DDS_FORMATS], - associated_type=DDSFormat, - groups=[ - DropDownGroup("Compressed", start_at="BC1_UNORM_SRGB"), - DropDownGroup("Uncompressed", start_at="R8G8B8A8_UNORM_SRGB"), - DropDownGroup("Legacy Compressed", start_at="DXT1"), - ], - ) - - -SUPPORTED_FORMATS = {f for f, _ in SUPPORTED_DDS_FORMATS} -SUPPORTED_BC7_FORMATS = list(SUPPORTED_FORMATS.intersection(BC7_FORMATS)) -SUPPORTED_BC123_FORMATS = list(SUPPORTED_FORMATS.intersection(BC123_FORMATS)) -SUPPORTED_WITH_ALPHA = list(SUPPORTED_FORMATS.intersection(WITH_ALPHA)) - - -class DDSErrorMetric(Enum): - PERCEPTUAL = 0 - UNIFORM = 1 - - -class BC7Compression(Enum): - BEST_SPEED = 1 - DEFAULT = 0 - BEST_QUALITY = 2 - - -def DdsMipMapsDropdown() -> DropDownInput: - return DropDownInput( - input_type="DdsMipMaps", - label="Generate Mip Maps", - preferred_style="checkbox", - options=[ - # these are not boolean values, see dds.py for more info - {"option": "Yes", "value": 0}, - {"option": "No", "value": 1}, - ], - ) - - -@io_group.register( - schema_id="chainner:image:save", - name="Save Image", - description="Save image to file at a specified directory.", - icon="MdSave", - inputs=[ - ImageInput().make_lazy(), - DirectoryInput(must_exist=False), - RelativePathInput("Subdirectory Path") - .make_optional() - .with_docs( - "An optional subdirectory path. Use this to save the image to a subdirectory of the specified directory. If the subdirectory does not exist, it will be created. Multiple subdirectories can be specified by separating them with a forward slash (`/`).", - "Example: `foo/bar`", - ), - RelativePathInput("Image Name").with_docs( - "The name of the image file **without** the file extension. If the file already exists, it will be overwritten.", - "Example: `my-image`", - ), - EnumInput( - ImageFormat, - "Image Format", - default=ImageFormat.PNG, - option_labels=IMAGE_FORMAT_LABELS, - ).with_id(4), - if_enum_group(4, ImageFormat.PNG)( - EnumInput( - PngColorDepth, - "Color Depth", - default=PngColorDepth.U8, - option_labels={ - PngColorDepth.U8: "8 Bits/Channel", - PngColorDepth.U16: "16 Bits/Channel", - }, - ).with_id(15), - ), - if_enum_group(4, ImageFormat.WEBP)( - BoolInput("Lossless", default=False).with_id(14), - ), - if_group( - Condition.enum(4, ImageFormat.JPG) - | Condition.enum(4, ImageFormat.AVIF) - | (Condition.enum(4, ImageFormat.WEBP) & Condition.enum(14, 0)) - )( - SliderInput( - "Quality", - minimum=0, - maximum=100, - default=95, - slider_step=1, - ).with_id(5), - ), - if_enum_group(4, ImageFormat.JPG)( - EnumInput( - JpegSubsampling, - label="Chroma Subsampling", - default=JpegSubsampling.FACTOR_422, - option_labels={ - JpegSubsampling.FACTOR_444: "4:4:4 (Best Quality)", - JpegSubsampling.FACTOR_440: "4:4:0", - JpegSubsampling.FACTOR_422: "4:2:2", - JpegSubsampling.FACTOR_420: "4:2:0 (Best Compression)", - }, - ).with_id(11), - BoolInput("Progressive", default=False).with_id(12), - ), - if_enum_group(4, ImageFormat.TIFF)( - EnumInput( - TiffColorDepth, - "Color Depth", - default=TiffColorDepth.U8, - option_labels={ - TiffColorDepth.U8: "8 Bits/Channel", - TiffColorDepth.U16: "16 Bits/Channel", - TiffColorDepth.F32: "32 Bits/Channel (Float)", - }, - ).with_id(16), - ), - if_enum_group(4, ImageFormat.DDS)( - DdsFormatDropdown().with_id(6), - if_enum_group(6, SUPPORTED_BC7_FORMATS)( - EnumInput( - BC7Compression, - label="BC7 Compression", - default=BC7Compression.DEFAULT, - ).with_id(7), - ), - if_enum_group(6, SUPPORTED_BC123_FORMATS)( - EnumInput(DDSErrorMetric, label="Error Metric").with_id(9), - BoolInput("Dithering", default=False).with_id(8), - ), - DdsMipMapsDropdown() - .with_id(10) - .with_docs( - "Whether [mipmaps](https://en.wikipedia.org/wiki/Mipmap) will be generated." - " Mipmaps vastly improve the quality of the image when it is viewed at a smaller size, but they also increase the file size by 33%." - ), - if_group( - Condition.enum(6, SUPPORTED_WITH_ALPHA) - & Condition.enum(10, 0) - & Condition.type(0, "Image { channels: 4 }") - )( - BoolInput("Separate Alpha for MipMaps", default=False) - .with_id(13) - .with_docs( - "Enable this option when the alpha channel of an image is **not** transparency.", - "The normal method for downscaling images with an alpha channel will remove the color information of transparent pixels (setting them to black). This is a problem if the alpha channel isn't transparency. E.g. games commonly store extra material information in the alpha channel of normal maps. Downscaling color and alpha separately fixes this problem.", - "Note: Do not enable this option if the alpha channel is transparency. Otherwise, dark artifacts may appear around transparency edges.", - ), - ), - ), - if_enum_group(4, ImageFormat.AVIF)( - EnumInput( - AvifSubsampling, - label="Chroma Subsampling", - default=AvifSubsampling.FACTOR_420, - option_labels={ - AvifSubsampling.FACTOR_444: "4:4:4 (Best Quality)", - AvifSubsampling.FACTOR_422: "4:2:2", - AvifSubsampling.FACTOR_420: "4:2:0", - AvifSubsampling.FACTOR_400: "4:0:0 (Best Compression)", - }, - ).with_id(17) - ), - BoolInput("Skip existing files", default=False) - .with_id(1000) - .with_docs( - "If enabled, the node will not overwrite existing files.", - ), - ], - outputs=[], - key_info=KeyInfo.enum(4), - side_effects=True, - limited_to_8bpc="Image will be saved with 8 bits/channel by default. Some formats support higher bit depths.", -) -def save_image_node( - lazy_image: Lazy[np.ndarray], - base_directory: Path, - relative_path: str | None, - filename: str, - image_format: ImageFormat, - png_color_depth: PngColorDepth, - webp_lossless: bool, - quality: int, - jpeg_chroma_subsampling: JpegSubsampling, - jpeg_progressive: bool, - tiff_color_depth: TiffColorDepth, - dds_format: DDSFormat, - dds_bc7_compression: BC7Compression, - dds_error_metric: DDSErrorMetric, - dds_dithering: bool, - dds_mipmap_levels: int, - dds_separate_alpha: bool, - avif_chroma_subsampling: AvifSubsampling, - skip_existing_files: bool, -) -> None: - full_path = get_full_path(base_directory, relative_path, filename, image_format) - - if full_path.exists(): - if skip_existing_files: - logger.debug(f"Skipping existing file: {full_path}") - return - else: - # Create directory if it doesn't exist - full_path.parent.mkdir(parents=True, exist_ok=True) - - logger.debug(f"Writing image to path: {full_path}") - img = lazy_image.value - - # DDS files are handled separately - if image_format == ImageFormat.DDS: - # we only support 8bits of precision for DDS - img = to_uint8(img, normalized=True) - - # remap legacy DX9 formats - legacy_dds = dds_format in LEGACY_TO_DXGI or dds_format in PREFER_DX9 - - save_as_dds( - full_path, - img, - to_dxgi(dds_format), - mipmap_levels=dds_mipmap_levels, - dithering=dds_dithering, - uniform_weighting=dds_error_metric == DDSErrorMetric.UNIFORM, - minimal_compression=dds_bc7_compression == BC7Compression.BEST_SPEED, - maximum_compression=dds_bc7_compression == BC7Compression.BEST_QUALITY, - dx9=legacy_dds, - separate_alpha=dds_separate_alpha, - ) - return - - # Some formats are handled by PIL - if image_format in (ImageFormat.GIF, ImageFormat.TGA, ImageFormat.AVIF): - # we only support 8bits of precision for those formats - img = to_uint8(img, normalized=True) - args = {} - - if image_format == ImageFormat.AVIF: - args["quality"] = quality - args["subsampling"] = avif_chroma_subsampling.value - - channels = get_h_w_c(img)[2] - if channels == 1: - # PIL supports grayscale images just fine, so we don't need to do any conversion - pass - elif channels == 3: - img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - elif channels == 4: - img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGBA) - else: - raise RuntimeError( - f"Unsupported number of channels. Saving .{image_format.extension} images is only supported for " - f"grayscale, RGB, and RGBA images." - ) - - with Image.fromarray(img) as image: - image.save(full_path, **args) - - else: - params: list[int] - if image_format == ImageFormat.JPG: - params = [ - cv2.IMWRITE_JPEG_QUALITY, - quality, - cv2.IMWRITE_JPEG_SAMPLING_FACTOR, - jpeg_chroma_subsampling.value, - cv2.IMWRITE_JPEG_PROGRESSIVE, - int(jpeg_progressive), - ] - elif image_format == ImageFormat.WEBP: - params = [cv2.IMWRITE_WEBP_QUALITY, 101 if webp_lossless else quality] - else: - params = [] - - # the bit depth depends on the image format and settings - precision: Literal["u8", "u16", "f32"] = "u8" - if image_format == ImageFormat.PNG: - if png_color_depth == PngColorDepth.U16: - precision = "u16" - elif image_format == ImageFormat.TIFF: - if tiff_color_depth == TiffColorDepth.U16: - precision = "u16" - elif tiff_color_depth == TiffColorDepth.F32: - precision = "f32" - - if precision == "u8": - img = to_uint8(img, normalized=True) - elif precision == "u16": - img = to_uint16(img, normalized=True) - elif precision == "f32": - # chainner images are always f32 - pass - - cv_save_image(full_path, img, params) - - -def get_full_path( - base_directory: Path, - relative_path: str | None, - filename: str, - image_format: ImageFormat, -) -> Path: - file = f"{filename}.{image_format.extension}" - if relative_path and relative_path != ".": - base_directory = base_directory / relative_path - full_path = base_directory / file - return full_path.resolve() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/view_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/view_image.py deleted file mode 100644 index d148d0a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/view_image.py +++ /dev/null @@ -1,28 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import LargeImageOutput - -from .. import io_group - - -@io_group.register( - schema_id="chainner:image:view", - name="View Image", - description="See an inline preview of the image in the editor.", - icon="BsEyeFill", - inputs=[ImageInput()], - outputs=[ - LargeImageOutput( - "Preview", - image_type="Input0", - has_handle=False, - assume_normalized=True, - ), - ], - side_effects=True, -) -def view_image_node(img: np.ndarray): - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/view_image_external.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/view_image_external.py deleted file mode 100644 index a2ee342..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/io/view_image_external.py +++ /dev/null @@ -1,48 +0,0 @@ -from __future__ import annotations - -import os -import platform -import subprocess -import time -from tempfile import mkdtemp - -import cv2 -import numpy as np -from sanic.log import logger - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ImageInput - -from .. import io_group - - -@io_group.register( - schema_id="chainner:image:preview", - name="View Image (external)", - description=[ - "Open the image in your default image viewer.", - "This works by saving a temporary file that will be deleted after chaiNNer is closed. It is not recommended to be used when performing batch processing.", - ], - icon="BsEyeFill", - inputs=[ImageInput()], - outputs=[], - side_effects=True, - limited_to_8bpc="The temporary file is an 8-bit PNG.", -) -def view_image_external_node(img: np.ndarray) -> None: - tempdir = mkdtemp(prefix="chaiNNer-") - logger.debug(f"Writing image to temp path: {tempdir}") - im_name = f"{time.time()}.png" - temp_save_dir = os.path.join(tempdir, im_name) - status = cv2.imwrite( - temp_save_dir, - to_uint8(img, normalized=True), - ) - - if status: - if platform.system() == "Darwin": # macOS - subprocess.call(("open", temp_save_dir)) - elif platform.system() == "Windows": # Windows - os.startfile(temp_save_dir) # type: ignore - else: # linux variants - subprocess.call(("xdg-open", temp_save_dir)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/video_frames/load_video.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/video_frames/load_video.py deleted file mode 100644 index cb32d0d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/video_frames/load_video.py +++ /dev/null @@ -1,87 +0,0 @@ -from __future__ import annotations - -from pathlib import Path -from typing import Any - -import numpy as np - -from api import Iterator, IteratorOutputInfo, NodeContext -from nodes.groups import Condition, if_group -from nodes.impl.ffmpeg import FFMpegEnv -from nodes.impl.video import VideoLoader -from nodes.properties.inputs import BoolInput, NumberInput, VideoFileInput -from nodes.properties.outputs import ( - AudioStreamOutput, - DirectoryOutput, - FileNameOutput, - ImageOutput, - NumberOutput, -) -from nodes.utils.utils import split_file_path - -from .. import video_frames_group - - -@video_frames_group.register( - schema_id="chainner:image:load_video", - name="Load Video", - description=[ - "Iterate over all frames in a video as images.", - "Uses FFMPEG to read video files.", - "This iterator is much slower than just using FFMPEG directly, so if you are doing a simple conversion, just use FFMPEG outside chaiNNer instead.", - ], - icon="MdVideoCameraBack", - inputs=[ - VideoFileInput(primary_input=True), - BoolInput("Use limit", default=False), - if_group(Condition.bool(1, True))( - NumberInput("Limit", default=10, minimum=1).with_docs( - "Limit the number of frames to iterate over. This can be useful for testing the iterator without having to iterate over all frames of the video." - " Will not copy audio if limit is used." - ) - ), - ], - outputs=[ - ImageOutput("Frame Image", channels=3), - NumberOutput( - "Frame Index", - output_type="if Input1 { min(uint, Input2 - 1) } else { uint }", - ).with_docs("A counter that starts at 0 and increments by 1 for each frame."), - DirectoryOutput("Video Directory", of_input=0), - FileNameOutput("Name", of_input=0), - NumberOutput("FPS", output_type="0.."), - AudioStreamOutput().suggest(), - ], - iterator_outputs=IteratorOutputInfo(outputs=[0, 1]), - node_context=True, - kind="newIterator", -) -def load_video_node( - node_context: NodeContext, - path: Path, - use_limit: bool, - limit: int, -) -> tuple[Iterator[tuple[np.ndarray, int]], Path, str, float, Any]: - video_dir, video_name, _ = split_file_path(path) - - loader = VideoLoader(path, FFMpegEnv.get_integrated(node_context.storage_dir)) - frame_count = loader.metadata.frame_count - if use_limit: - frame_count = min(frame_count, limit) - - audio_stream = loader.get_audio_stream() - - def iterator(): - for index, frame in enumerate(loader.stream_frames()): - yield frame, index - - if use_limit and index + 1 >= limit: - break - - return ( - Iterator.from_iter(iter_supplier=iterator, expected_length=frame_count), - video_dir, - video_name, - loader.metadata.fps, - audio_stream, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/video_frames/save_video.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/video_frames/save_video.py deleted file mode 100644 index 9af8804..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image/video_frames/save_video.py +++ /dev/null @@ -1,406 +0,0 @@ -from __future__ import annotations - -import os -from dataclasses import dataclass -from enum import Enum -from pathlib import Path -from subprocess import Popen -from typing import Any, Literal - -import ffmpeg -import numpy as np -from sanic.log import logger - -from api import Collector, IteratorInputInfo, KeyInfo, NodeContext -from nodes.groups import Condition, if_enum_group, if_group -from nodes.impl.ffmpeg import FFMpegEnv -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ( - BoolInput, - DirectoryInput, - EnumInput, - ImageInput, - RelativePathInput, - SliderInput, - TextInput, -) -from nodes.properties.inputs.generic_inputs import AudioStreamInput -from nodes.properties.inputs.numeric_inputs import NumberInput -from nodes.utils.utils import get_h_w_c - -from .. import video_frames_group - - -class VideoFormat(Enum): - MKV = "mkv" - MP4 = "mp4" - MOV = "mov" - WEBM = "webm" - AVI = "avi" - GIF = "gif" - - @property - def ext(self) -> str: - return self.value - - @property - def encoders(self) -> tuple[VideoEncoder, ...]: - if self == VideoFormat.MKV: - return ( - VideoEncoder.H264, - VideoEncoder.H265, - VideoEncoder.VP9, - VideoEncoder.FFV1, - ) - elif self == VideoFormat.MP4: - return VideoEncoder.H264, VideoEncoder.H265, VideoEncoder.VP9 - elif self == VideoFormat.MOV: - return VideoEncoder.H264, VideoEncoder.H265 - elif self == VideoFormat.WEBM: - return (VideoEncoder.VP9,) - elif self == VideoFormat.AVI: - return (VideoEncoder.H264,) - elif self == VideoFormat.GIF: - return () - else: - raise ValueError(f"Unknown container: {self}") - - -class VideoEncoder(Enum): - H264 = "libx264" - H265 = "libx265" - VP9 = "libvpx-vp9" - FFV1 = "ffv1" - - @property - def formats(self) -> tuple[VideoFormat, ...]: - formats: list[VideoFormat] = [] - for format in VideoFormat: - if self in format.encoders: - formats.append(format) - return tuple(formats) - - -class VideoPreset(Enum): - ULTRA_FAST = "ultrafast" - SUPER_FAST = "superfast" - VERY_FAST = "veryfast" - FAST = "fast" - MEDIUM = "medium" - SLOW = "slow" - SLOWER = "slower" - VERY_SLOW = "veryslow" - - -class AudioSettings(Enum): - AUTO = "auto" - COPY = "copy" - TRANSCODE = "transcode" - - -PARAMETERS: dict[VideoEncoder, list[Literal["preset", "crf"]]] = { - VideoEncoder.H264: ["preset", "crf"], - VideoEncoder.H265: ["preset", "crf"], - VideoEncoder.VP9: ["crf"], - VideoEncoder.FFV1: [], -} - - -@dataclass -class Writer: - container: VideoFormat - encoder: VideoEncoder | None - fps: float - audio: object | None - audio_settings: AudioSettings - save_path: str - output_params: dict[str, str | float] - global_params: list[str] - ffmpeg_env: FFMpegEnv - out: Popen | None = None - - def start(self, width: int, height: int): - # Create the writer and run process - if self.out is None: - # Verify some parameters - if self.encoder in (VideoEncoder.H264, VideoEncoder.H265): - assert ( - height % 2 == 0 and width % 2 == 0 - ), f'The "{self.encoder.value}" encoder requires an even-number frame resolution.' - - try: - self.out = ( - ffmpeg.input( - "pipe:", - format="rawvideo", - pix_fmt="bgr24", - s=f"{width}x{height}", - r=self.fps, - loglevel="error", - ) - .output(**self.output_params, loglevel="error") - .overwrite_output() - .global_args(*self.global_params) - .run_async( - pipe_stdin=True, pipe_stdout=False, cmd=self.ffmpeg_env.ffmpeg - ) - ) - - except Exception as e: - logger.warning("Failed to open video writer", exc_info=e) - - def write_frame(self, img: np.ndarray): - # Create the writer and run process - if self.out is None: - h, w, _ = get_h_w_c(img) - self.start(w, h) - - out_frame = to_uint8(img, normalized=True) - if self.out is not None and self.out.stdin is not None: - self.out.stdin.write(out_frame.tobytes()) - else: - raise RuntimeError("Failed to open video writer") - - def close(self): - if self.out is not None: - if self.out.stdin is not None: - self.out.stdin.close() - self.out.wait() - - if self.audio is not None: - video_path = self.save_path - base, ext = os.path.splitext(video_path) - audio_video_path = f"{base}_av{ext}" - - # Default and auto -> copy - output_params = { - "vcodec": "copy", - "acodec": "copy", - } - if self.container == VideoFormat.WEBM: - if self.audio_settings in (AudioSettings.TRANSCODE, AudioSettings.AUTO): - output_params["acodec"] = "libopus" - output_params["b:a"] = "320k" - else: - raise ValueError(f"WebM does not support {self.audio_settings}") - elif self.audio_settings == AudioSettings.TRANSCODE: - output_params["acodec"] = "aac" - output_params["b:a"] = "320k" - - try: - video_stream = ffmpeg.input(video_path) - output_video = ffmpeg.output( - self.audio, - video_stream, - audio_video_path, - **output_params, - ).overwrite_output() - ffmpeg.run(output_video) - # delete original, rename new - os.remove(video_path) - os.rename(audio_video_path, video_path) - except Exception: - logger.warning( - "Failed to copy audio to video, input file probably contains " - "no audio or audio stream is supported by this container. Ignoring audio settings." - ) - try: - os.remove(audio_video_path) - except Exception: - pass - - -@video_frames_group.register( - schema_id="chainner:image:save_video", - name="Save Video", - description=[ - "Combines an iterable sequence into a video, which it saves to a file.", - "Uses FFMPEG to write video files.", - "This iterator is much slower than just using FFMPEG directly, so if you are doing a simple conversion, just use FFMPEG outside chaiNNer instead.", - ], - icon="MdVideoCameraBack", - inputs=[ - ImageInput("Image Sequence", channels=3), - DirectoryInput(must_exist=False), - RelativePathInput("Video Name"), - EnumInput( - VideoFormat, - label="Video Format", - option_labels={ - VideoFormat.MKV: "mkv", - VideoFormat.MP4: "mp4", - VideoFormat.MOV: "mov", - VideoFormat.WEBM: "WebM", - VideoFormat.AVI: "avi", - VideoFormat.GIF: "GIF", - }, - ).with_id(4), - EnumInput( - VideoEncoder, - label="Encoder", - option_labels={ - VideoEncoder.H264: "H.264 (AVC)", - VideoEncoder.H265: "H.265 (HEVC)", - VideoEncoder.VP9: "VP9", - VideoEncoder.FFV1: "FFV1", - }, - conditions={ - VideoEncoder.H264: Condition.enum(4, VideoEncoder.H264.formats), - VideoEncoder.H265: Condition.enum(4, VideoEncoder.H265.formats), - VideoEncoder.VP9: Condition.enum(4, VideoEncoder.VP9.formats), - VideoEncoder.FFV1: Condition.enum(4, VideoEncoder.FFV1.formats), - }, - ) - .with_id(3) - .wrap_with_conditional_group(), - if_enum_group(3, (VideoEncoder.H264, VideoEncoder.H265))( - EnumInput(VideoPreset, default=VideoPreset.MEDIUM) - .with_docs( - "For more information on presets, see [here](https://trac.ffmpeg.org/wiki/Encode/H.264#Preset)." - ) - .with_id(8), - ), - if_enum_group(3, (VideoEncoder.H264, VideoEncoder.H265, VideoEncoder.VP9))( - SliderInput( - "Quality (CRF)", - precision=0, - controls_step=1, - slider_step=1, - minimum=0, - maximum=51, - default=23, - ends=("Best", "Worst"), - ) - .with_docs( - "For more information on CRF, see [here](https://trac.ffmpeg.org/wiki/Encode/H.264#crf)." - ) - .with_id(9), - ), - BoolInput("Additional parameters", default=False) - .with_docs( - "Allow user to add FFmpeg parameters. [Link to FFmpeg documentation](https://ffmpeg.org/documentation.html)." - ) - .with_id(12), - if_group(Condition.bool(12, True))( - TextInput( - "Additional parameters", - multiline=True, - label_style="hidden", - allow_empty_string=True, - has_handle=False, - ) - .make_optional() - .with_id(13) - ), - NumberInput( - "FPS", default=30, minimum=1, controls_step=1, has_handle=True, precision=4 - ).with_id(14), - if_group(~Condition.enum(4, VideoFormat.GIF))( - AudioStreamInput().make_optional().with_id(15).suggest(), - if_group(Condition.type(15, "AudioStream"))( - EnumInput( - AudioSettings, - label="Audio", - default=AudioSettings.AUTO, - conditions={ - AudioSettings.COPY: ~Condition.enum(4, VideoFormat.WEBM) - }, - ) - .with_docs( - "The first audio stream can be discarded, copied or transcoded at 320 kb/s." - " Some audio formats are not supported by selected container, thus copying the audio may fail." - " Some players may not output the audio stream if its format is not supported." - " If it isn't working for you, verify compatibility or use FFMPEG to mux the audio externally." - ) - .with_id(10) - ), - ), - ], - iterator_inputs=IteratorInputInfo(inputs=0), - outputs=[], - key_info=KeyInfo.enum(4), - kind="collector", - side_effects=True, - node_context=True, -) -def save_video_node( - node_context: NodeContext, - _: None, - save_dir: Path, - video_name: str, - container: VideoFormat, - encoder: VideoEncoder, - video_preset: VideoPreset, - crf: int, - advanced: bool, - additional_parameters: str | None, - fps: float, - audio: Any, - audio_settings: AudioSettings, -) -> Collector[np.ndarray, None]: - save_path = (save_dir / f"{video_name}.{container.ext}").resolve() - save_path.parent.mkdir(parents=True, exist_ok=True) - - # Common output settings - output_params = { - "filename": str(save_path), - "pix_fmt": "yuv420p", - "r": fps, - "movflags": "faststart", - } - - # Append parameters - if encoder in container.encoders: - output_params["vcodec"] = encoder.value - - parameters = PARAMETERS[encoder] - if "preset" in parameters: - output_params["preset"] = video_preset.value - if "crf" in parameters: - output_params["crf"] = crf - - # Append additional parameters - global_params: list[str] = [] - if advanced and additional_parameters is not None: - additional_parameters = " " + " ".join(additional_parameters.split()) - additional_parameters_array = additional_parameters.split(" -")[1:] - non_overridable_params = ["filename", "vcodec", "crf", "preset", "c:"] - for parameter in additional_parameters_array: - key, value = parameter, None - try: - key, value = parameter.split(" ") - except Exception: - pass - - if value is not None: - for nop in non_overridable_params: - if not key.startswith(nop): - output_params[key] = value - else: - raise ValueError(f"Duplicate parameter: -{parameter}") - else: - global_params.append(f"-{parameter}") - - # Audio - if container == VideoFormat.GIF: - audio = None - - writer = Writer( - container=container, - encoder=encoder, - fps=fps, - audio=audio, - audio_settings=audio_settings, - save_path=str(save_path), - output_params=output_params, - global_params=global_params, - ffmpeg_env=FFMpegEnv.get_integrated(node_context.storage_dir), - ) - - def on_iterate(img: np.ndarray): - writer.write_frame(img) - - def on_complete(): - writer.close() - - return Collector(on_iterate=on_iterate, on_complete=on_complete) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/__init__.py deleted file mode 100644 index 793ab5c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from .. import image_adjustments_category - -adjustments_group = image_adjustments_category.add_node_group("Adjustments") -threshold_group = image_adjustments_category.add_node_group("Threshold") -gamma_group = image_adjustments_category.add_node_group("Gamma") -arithmetic_group = image_adjustments_category.add_node_group("Arithmetic") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/add.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/add.py deleted file mode 100644 index 13d7469..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/add.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:add", - description="Add values to an image.", - name="Add", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Add", - minimum=-100, - maximum=100, - default=0, - precision=1, - controls_step=1, - ), - ], - outputs=[ImageOutput(image_type="Input0")], -) -def add_node(img: np.ndarray, add: float) -> np.ndarray: - if add == 0: - return img - - img = img + (add / 100) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/brightness_and_contrast.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/brightness_and_contrast.py deleted file mode 100644 index 070369b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/brightness_and_contrast.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:brightness_and_contrast", - description="Adjust the brightness and contrast of an image.", - name="Brightness & Contrast", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Brightness", - minimum=-100, - maximum=100, - default=0, - precision=1, - controls_step=1, - ), - SliderInput( - "Contrast", - minimum=-100, - maximum=100, - default=0, - precision=1, - controls_step=1, - ), - ], - outputs=[ - ImageOutput(shape_as=0, assume_normalized=True), - ], -) -def brightness_and_contrast_node( - img: np.ndarray, brightness: float, contrast: float -) -> np.ndarray: - brightness /= 100 - contrast /= 100 - - if brightness == 0 and contrast == 0: - return img - - _, _, c = get_h_w_c(img) - - # Contrast correction factor - max_c = 259 / 255 - factor: float = (max_c * (contrast + 1)) / (max_c - contrast) - add: float = factor * brightness + 0.5 * (1 - factor) - - def process_rgb(rgb: np.ndarray): - if factor == 1: - out = rgb + add - else: - out = factor * rgb - out += add - - if add < 0 or factor + add > 1: - out = np.clip(out, 0, 1, out=out) - - return out - - if c <= 3: - return process_rgb(img) - else: - return np.dstack([process_rgb(img[:, :, :3]), img[:, :, 3:]]) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/clamp.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/clamp.py deleted file mode 100644 index 390e47e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/clamp.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:clamp", - name="Clamp", - description="Clamps the values of an image.", - icon="ImContrast", - inputs=[ - ImageInput(), - SliderInput( - "Minimum", - minimum=0.0, - maximum=1.0, - default=0.0, - precision=4, - controls_step=0.001, - scale="log", - ), - SliderInput( - "Maximum", - minimum=0.0, - maximum=1.0, - default=1.0, - precision=4, - controls_step=0.001, - scale="log", - ), - ], - outputs=[ - ImageOutput(shape_as=0, assume_normalized=True), - ], -) -def clamp_node(img: np.ndarray, minimum: float, maximum: float) -> np.ndarray: - if minimum <= 0 and maximum >= 1: - return img - return np.clip(img, minimum, maximum) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/color_levels.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/color_levels.py deleted file mode 100644 index 7c541d7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/color_levels.py +++ /dev/null @@ -1,113 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.groups import icon_set_group -from nodes.impl.image_utils import as_3d -from nodes.properties.inputs import BoolInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:color_levels", - name="Color Levels", - description="Color Levels can be used to make an image lighter or darker, to change contrast or to correct a predominant color cast.", - icon="MdOutlineColorLens", - inputs=[ - ImageInput(channels=[1, 3, 4]), - icon_set_group("Channels")( - BoolInput("Red", default=True), - BoolInput("Green", default=True), - BoolInput("Blue", default=True), - BoolInput("Alpha", default=False), - ), - SliderInput( - "In Black", - minimum=0, - maximum=1, - default=0, - precision=3, - controls_step=0.01, - ), - SliderInput( - "In White", - minimum=0, - maximum=1, - default=1, - precision=3, - controls_step=0.01, - ), - SliderInput( - "Gamma", - minimum=0, - maximum=10, - default=1, - precision=3, - controls_step=0.01, - scale="log", - ), - SliderInput( - "Out Black", - minimum=0, - maximum=1, - default=0, - precision=3, - controls_step=0.01, - ), - SliderInput( - "Out White", - minimum=0, - maximum=1, - default=1, - precision=3, - controls_step=0.01, - ), - ], - outputs=[ - ImageOutput(shape_as=0), - ], -) -def color_levels_node( - img: np.ndarray, - red: bool, - green: bool, - blue: bool, - alpha: bool, - in_black: float, - in_white: float, - in_gamma: float, - out_black: float, - out_white: float, -) -> np.ndarray: - # This code was adapted from a Stack-Overflow answer by Iperov, - # can found at: https://stackoverflow.com/a/60339950 - - _, _, c = get_h_w_c(img) - - if c == 1: - img = as_3d(img) - red, green, blue = True, True, True - - in_gamma = max(0.001, in_gamma) - - in_black_all = np.full(c, in_black, dtype="float32") - in_white_all = np.full(c, in_white, dtype="float32") - in_gamma_all = np.full(c, in_gamma, dtype="float32") - out_black_all = np.full(c, out_black, dtype="float32") - out_white_all = np.full(c, out_white, dtype="float32") - - selected_channels = [blue, green, red, alpha] if c == 4 else [blue, green, red] - - for i, channel in enumerate(selected_channels): - if not channel: - in_black_all[i], in_white_all[i], in_gamma_all[i] = 0, 1, 1 - out_black_all[i], out_white_all[i] = 0, 1 - - img = (img - in_black_all) / (in_white_all - in_black_all) - img = np.clip(img, 0, 1) - img = (img ** (1 / in_gamma_all)) * (out_white_all - out_black_all) + out_black_all - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/divide.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/divide.py deleted file mode 100644 index 6f2a30c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/divide.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:divide", - description="Divide all channels in an image by a value.", - name="Divide", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Divide", - minimum=0.0001, - maximum=4.0, - default=1.0, - precision=4, - controls_step=0.0001, - scale="log", - ), - ], - outputs=[ImageOutput(image_type="Input0")], -) -def divide_node(img: np.ndarray, divide: float) -> np.ndarray: - if divide == 1.0: - return img - - img = img * (1.0 / divide) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/gamma.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/gamma.py deleted file mode 100644 index 402eb3c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/gamma.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -import numpy as np -from chainner_ext import fast_gamma - -from nodes.properties.inputs import BoolInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:gamma", - name="Gamma", - description="Adjusts the gamma of an image.", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Gamma", - minimum=0.01, - maximum=100, - default=1, - precision=4, - controls_step=0.1, - scale="log", - ), - BoolInput("Invert Gamma", default=False), - ], - outputs=[ - ImageOutput( - image_type="Input0", - assume_normalized=True, - ) - ], -) -def gamma_node(img: np.ndarray, gamma: float, invert_gamma: bool) -> np.ndarray: - if gamma == 1: - # noop - return img - - if invert_gamma: - gamma = 1 / gamma - - return fast_gamma(img, gamma) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/generate_threshold.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/generate_threshold.py deleted file mode 100644 index a5ec735..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/generate_threshold.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from typing import Dict - -import cv2 -import numpy as np - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -class AutoThreshold(Enum): - OTSU = 0 - TRIANGLE = 1 - - -_AUTO_THRESHOLD_LABELS: Dict[AutoThreshold, str] = { - AutoThreshold.OTSU: "Otsu's Method", - AutoThreshold.TRIANGLE: "Triangle Method", -} - - -@adjustments_group.register( - schema_id="chainner:image:generate_threshold", - name="Generate Threshold", - description="Automatically determines an optimal threshold value for the given image.", - icon="MdShowChart", - inputs=[ - ImageInput(), - EnumInput(AutoThreshold, "Method", option_labels=_AUTO_THRESHOLD_LABELS), - ], - outputs=[ - NumberOutput("Threshold", output_type="0..100"), - ], - see_also=[ - "chainner:image:threshold", - ], -) -def generate_threshold_node(img: np.ndarray, method: AutoThreshold) -> float: - if get_h_w_c(img)[2] != 1: - # these methods need grayscale images, so we'll use the mean across all channels - img = np.mean(img, axis=-1) - - # otsu and triangle methods are only implemented for uint8 images - img = to_uint8(img, normalized=True) - - if method == AutoThreshold.OTSU: - threshold, _ = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) - elif method == AutoThreshold.TRIANGLE: - threshold, _ = cv2.threshold( - img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE - ) - - return threshold / 255 * 100 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/hue_and_saturation.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/hue_and_saturation.py deleted file mode 100644 index 3924598..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/hue_and_saturation.py +++ /dev/null @@ -1,128 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -def with_lightness(img: np.ndarray, lightness: float) -> np.ndarray: - if lightness > 0: - assert lightness <= 1 - res = img * (1 - lightness) - res += lightness - return res - elif lightness < 0: - assert lightness >= -1 - return img * (1 + lightness) - else: - return img - - -@adjustments_group.register( - schema_id="chainner:image:hue_and_saturation", - name="Hue & Saturation", - description="Adjust the hue and saturation of an image. This is performed in the HSV color-space.", - icon="MdOutlineColorLens", - inputs=[ - ImageInput(channels=[1, 3, 4]), - SliderInput( - "Hue", - minimum=-180, - maximum=180, - default=0, - precision=1, - controls_step=1, - gradient=[ - "#ff0000", - "#ffff00", - "#00ff00", - "#00ffff", - "#0000ff", - "#ff00ff", - "#ff0000", - ], - ), - SliderInput( - "Saturation", - minimum=-100, - maximum=100, - default=0, - precision=1, - controls_step=1, - # gradient from gray to saturated - gradient=["#808080", "#ff0000"], - ), - SliderInput( - "Lightness", - minimum=-100, - maximum=100, - default=0, - precision=1, - controls_step=1, - gradient=["#000000", "#ffffff"], - ), - ], - outputs=[ - ImageOutput(shape_as=0, assume_normalized=True), - ], -) -def hue_and_saturation_node( - img: np.ndarray, - hue: float, - saturation: float, - lightness: float, -) -> np.ndarray: - saturation /= 100 - lightness /= 100 - - _, _, c = get_h_w_c(img) - - # Pass through unadjusted images - if hue == 0 and saturation == 0 and lightness == 0: - return img - - if c == 1: - # Hue and saturation have no effect on grayscale, so we just need to adjust lightness - return with_lightness(img, lightness) - - # Preserve alpha channel if it exists - alpha = None - if c > 3: - alpha = img[:, :, 3] - img = img[:, :, :3] - - if hue != 0 or saturation != 0: - # Convert to HLS color space - h, l, s = cv2.split(cv2.cvtColor(img, cv2.COLOR_BGR2HLS)) - - # Adjust hue - if hue != 0: - h += hue # type: ignore - h[h >= 360] -= 360 # Wrap positive overflow - h[h < 0] += 360 # Wrap negative overflow - - # Adjust saturation - if saturation != 0: - factor = 1 + saturation - s *= factor # type: ignore - if factor > 1: - s = np.clip(s, 0, 1, out=s) - - # we assume that this returns normalized values in Change Color Model, - # so it should be fine here as well - img = cv2.cvtColor(cv2.merge([h, l, s]), cv2.COLOR_HLS2BGR) - - # Adjust lightness - if lightness != 0: - img = with_lightness(img, lightness) - - # Re-add alpha, if it exists - if alpha is not None: - img = np.dstack((img, alpha)) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/invert_color.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/invert_color.py deleted file mode 100644 index ea3a7d1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/invert_color.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:invert", - name="Invert Color", - description="Inverts all colors in an image.", - icon="MdInvertColors", - inputs=[ImageInput()], - outputs=[ImageOutput(shape_as=0, assume_normalized=True)], -) -def invert_color_node(img: np.ndarray) -> np.ndarray: - c = get_h_w_c(img)[2] - - # invert the first 3 channels - if c <= 3: - return 1 - img - - img = img.copy() - img[:, :, :3] = 1 - img[:, :, :3] - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/log_to_linear.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/log_to_linear.py deleted file mode 100644 index 97dbe1e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/log_to_linear.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import BoolInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:log2lin", - description="Convert all channels in an image to a scene linear encoding using the Kodak Cineon logarithmic function set.", - name="Log To Linear", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Black", - minimum=0.0, - maximum=1023, - default=95.0, - precision=1, - controls_step=1.0, - ), - SliderInput( - "White", - minimum=0.0, - maximum=1023, - default=685.0, - precision=1, - controls_step=1.0, - ), - SliderInput( - "Gamma", - minimum=0.0001, - maximum=1.0, - default=0.6, - precision=2, - controls_step=0.0001, - scale="log", - ), - BoolInput("Invert Log to Linear", default=False).with_docs( - "When checked this will convert the input image back to the Kodak Cineon Logarithmic encoding" - ), - ], - outputs=[ImageOutput(image_type="Input0")], -) -def log_to_linear_node( - img: np.ndarray, - black: float, - white: float, - gamma: float, - invert_log_to_linear: bool, -) -> np.ndarray: - offset = pow(10.0, (black - white) * 0.002 / gamma) - gain = 1.0 / (1.0 - offset) - - if not invert_log_to_linear: - img = gain * (pow(10.0, (1023.0 * img - white) * 0.002 / gamma) - offset) - else: - img = (np.log10(img / gain + offset) / (0.002 / gamma) + white) / 1023.0 - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/multiply.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/multiply.py deleted file mode 100644 index 2af525d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/multiply.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:multiply", - description="Multiply all channels in an image by a value.", - name="Multiply", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Multiply", - minimum=0.0, - maximum=4.0, - default=1.0, - precision=4, - controls_step=0.0001, - scale="log", - ), - ], - outputs=[ImageOutput(image_type="Input0")], -) -def multiply_node(img: np.ndarray, mult: float) -> np.ndarray: - if mult == 1.0: - return img - - img = img * mult - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/opacity.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/opacity.py deleted file mode 100644 index be9d4d0..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/opacity.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from api import KeyInfo -from nodes.impl.pil_utils import convert_to_bgra -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -@adjustments_group.register( - schema_id="chainner:image:opacity", - name="Opacity", - description="Adjusts the opacity of an image. The higher the opacity value, the more opaque the image is.", - icon="MdOutlineOpacity", - inputs=[ - ImageInput(), - SliderInput( - "Opacity", - maximum=100, - default=100, - precision=1, - controls_step=1, - unit="%", - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image(size_as="Input0"), - channels=4, - assume_normalized=True, - ) - ], - key_info=KeyInfo.number(1), -) -def opacity_node(img: np.ndarray, opacity: float) -> np.ndarray: - # Convert inputs - c = get_h_w_c(img)[2] - if opacity == 100 and c == 4: - return img - imgout = convert_to_bgra(img, c) - opacity /= 100 - - imgout[:, :, 3] *= opacity - - return imgout diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/stretch_contrast.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/stretch_contrast.py deleted file mode 100644 index 14e5ae3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/stretch_contrast.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import if_enum_group -from nodes.properties.inputs import BoolInput, EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import adjustments_group - - -def _stretch(img: np.ndarray, range_min: float, range_max: float) -> np.ndarray: - if range_min > range_max: - raise ValueError("min must be less than max") - if range_min == range_max: - return img * 0 - - range_diff = range_max - range_min - return (img - range_min) / range_diff - - -class StretchMode(Enum): - AUTO = 0 - PERCENTILE = 1 - MANUAL = 2 - - -@adjustments_group.register( - schema_id="chainner:image:stretch_contrast", - description="Automatically stretches the histogram values in the given image. This is similar to auto levels.", - name="Stretch Contrast", - icon="ImContrast", - inputs=[ - ImageInput(channels=[1, 3, 4]), - EnumInput(StretchMode, preferred_style="tabs").with_id(1), - if_enum_group(1, [StretchMode.AUTO, StretchMode.PERCENTILE])( - BoolInput("Keep Colors", default=True), - ), - if_enum_group(1, StretchMode.PERCENTILE)( - SliderInput( - "Percentile", minimum=0, maximum=50, default=1, precision=2, scale="log" - ), - ), - if_enum_group(1, StretchMode.MANUAL)( - SliderInput("Minimum", minimum=0, maximum=255, default=0, precision=1), - SliderInput("Maximum", minimum=0, maximum=255, default=255, precision=1), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let minMaxRangeValid: bool = match Input1 { - StretchMode::Manual => Input4 < Input5, - _ => true, - }; - - if minMaxRangeValid { - Input0 - } else { - error("Minimum must be less than Maximum.") - } - """, - ), - ], -) -def stretch_contrast_node( - img: np.ndarray, - mode: StretchMode, - keep_colors: bool, - percentile: float, - manual_min: float, - manual_max: float, -) -> np.ndarray: - def get_range_of(i: np.ndarray) -> tuple[float, float]: - if mode == StretchMode.AUTO: - return float(np.min(i)), float(np.max(i)) - elif mode == StretchMode.PERCENTILE: - return float(np.percentile(i, percentile)), float( - np.percentile(i, 100 - percentile) - ) - elif mode == StretchMode.MANUAL: - if manual_min > manual_max: - raise ValueError("Minimum must be less than Maximum") - return manual_min / 255, manual_max / 255 - - _, _, c = get_h_w_c(img) - - alpha = None - if c == 4: - alpha = img[:, :, 3] - img = img[:, :, :3] - - if keep_colors or mode == StretchMode.MANUAL or c == 1: - # stretch all channels together - - range_min, range_max = get_range_of(img) - img = _stretch(img, range_min, range_max) - else: - # stretch all channels individually - - channels: list[np.ndarray] = [] - for i in range(get_h_w_c(img)[2]): - channel = img[:, :, i] - - range_min, range_max = get_range_of(channel) - channels.append(_stretch(channel, range_min, range_max)) - - img = np.dstack(channels) - - if alpha is not None: - img = np.dstack([img, alpha]) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/threshold.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/threshold.py deleted file mode 100644 index 83e5a5f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/threshold.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from typing import Dict - -import cv2 -import numpy as np -from chainner_ext import binary_threshold - -from nodes.groups import if_enum_group -from nodes.properties.inputs import BoolInput, EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -class ThresholdType(Enum): - BINARY = cv2.THRESH_BINARY - BINARY_INV = cv2.THRESH_BINARY_INV - TRUNC = cv2.THRESH_TRUNC - TO_ZERO = cv2.THRESH_TOZERO - TO_ZERO_INV = cv2.THRESH_TOZERO_INV - - -_THRESHOLD_TYPE_LABELS: Dict[ThresholdType, str] = { - ThresholdType.BINARY: "Binary", - ThresholdType.BINARY_INV: "Binary (Inverted)", - ThresholdType.TRUNC: "Truncated", - ThresholdType.TO_ZERO: "To Zero", - ThresholdType.TO_ZERO_INV: "To Zero (Inverted)", -} - - -@adjustments_group.register( - schema_id="chainner:image:threshold", - name="Threshold", - description="Replaces pixels based on the threshold value. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to the maximum value.", - icon="MdShowChart", - inputs=[ - ImageInput(), - SliderInput( - "Threshold", - maximum=100, - default=50, - precision=1, - controls_step=1, - ), - EnumInput( - ThresholdType, - "Threshold Type", - default=ThresholdType.BINARY, - option_labels=_THRESHOLD_TYPE_LABELS, - ).with_id(3), - if_enum_group(3, (ThresholdType.BINARY, ThresholdType.BINARY_INV))( - SliderInput( - "Maximum Value", - maximum=100, - default=100, - precision=1, - controls_step=1, - ).with_id(2), - ), - BoolInput("Anti-aliasing", default=False).with_docs( - "Enables sub-pixel precision. Bilinear interpolation is used to fill in values in between pixels.", - "Conceptually, the option is equivalent to first upscaling the image by a factor of X (with linear interpolation), thresholding it, and then downscaling it by a factor of X (where X is 20 or more).", - ), - ], - outputs=[ImageOutput(image_type="Input0")], - see_also=[ - "chainner:image:generate_threshold", - "chainner:image:threshold_adaptive", - ], -) -def threshold_node( - img: np.ndarray, - threshold: float, - thresh_type: ThresholdType, - max_value: float, - anti_aliasing: bool, -) -> np.ndarray: - threshold /= 100 - max_value /= 100 - - if not anti_aliasing: - _, result = cv2.threshold(img, threshold, max_value, thresh_type.value) - return result - - binary = binary_threshold(img, threshold, True) - if thresh_type == ThresholdType.BINARY_INV: - binary = 1 - binary - - if thresh_type == ThresholdType.BINARY or thresh_type == ThresholdType.BINARY_INV: - if max_value < 1: - binary *= max_value - return binary - elif thresh_type == ThresholdType.TRUNC: - return binary * threshold + img * (1 - binary) - elif thresh_type == ThresholdType.TO_ZERO: - return binary * img - elif thresh_type == ThresholdType.TO_ZERO_INV: - return (1 - binary) * img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/threshold_adaptive.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/threshold_adaptive.py deleted file mode 100644 index 5f3dffa..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/adjustments/threshold_adaptive.py +++ /dev/null @@ -1,101 +0,0 @@ -from __future__ import annotations - -from enum import Enum -from typing import Dict - -import cv2 -import numpy as np - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import adjustments_group - - -class AdaptiveThresholdType(Enum): - BINARY = cv2.THRESH_BINARY - BINARY_INV = cv2.THRESH_BINARY_INV - - -_THRESHOLD_TYPE_LABELS: Dict[AdaptiveThresholdType, str] = { - AdaptiveThresholdType.BINARY: "Binary", - AdaptiveThresholdType.BINARY_INV: "Binary (Inverted)", -} - - -class AdaptiveMethod(Enum): - MEAN = cv2.ADAPTIVE_THRESH_MEAN_C - GAUSSIAN = cv2.ADAPTIVE_THRESH_GAUSSIAN_C - - -_ADAPTIVE_METHOD_LABELS: Dict[AdaptiveMethod, str] = { - AdaptiveMethod.MEAN: "Mean", - AdaptiveMethod.GAUSSIAN: "Gaussian", -} - - -@adjustments_group.register( - schema_id="chainner:image:threshold_adaptive", - name="Threshold (Adaptive)", - description="Similar to regular threshold, but determines the threshold for a pixel based on a small region around it.", - icon="MdAutoGraph", - inputs=[ - ImageInput(channels=1), - EnumInput( - AdaptiveThresholdType, - "Threshold Type", - default=AdaptiveThresholdType.BINARY, - option_labels=_THRESHOLD_TYPE_LABELS, - ).with_id(3), - SliderInput( - "Maximum Value", - maximum=100, - default=100, - precision=1, - controls_step=1, - ).with_id(1), - EnumInput( - AdaptiveMethod, - "Adaptive Method", - default=AdaptiveMethod.MEAN, - option_labels=_ADAPTIVE_METHOD_LABELS, - ).with_id(2), - NumberInput("Block Radius", default=1, minimum=1).with_id(4), - NumberInput( - "Constant Subtraction", - minimum=-100, - maximum=100, - default=0, - precision=1, - ) - .with_id(5) - .with_docs( - "A constant value that is subtracted from the automatically determined adaptive threshold.", - "Assuming that **Threshold Type** is *Binary*, then higher values will result in more white pixels and lower values will result in more black pixels.", - ), - ], - outputs=[ImageOutput(image_type="Input0")], - limited_to_8bpc=True, -) -def threshold_adaptive_node( - img: np.ndarray, - threshold_type: AdaptiveThresholdType, - max_value: float, - adaptive_method: AdaptiveMethod, - block_radius: int, - c: float, -) -> np.ndarray: - # Adaptive threshold requires uint8 input - img = to_uint8(img, normalized=True) - - max_value = max_value / 100 * 255 - - return cv2.adaptiveThreshold( - img, - max_value, - adaptive_method.value, - threshold_type.value, - block_radius * 2 + 1, - round(c / 100 * 255), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/add.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/add.py deleted file mode 100644 index 51ee0b8..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/add.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import arithmetic_group - - -@arithmetic_group.register( - schema_id="chainner:image:add", - description="Add values to an image.", - name="Add", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Add", - minimum=-100, - maximum=100, - default=0, - precision=1, - controls_step=1, - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def add_node(img: np.ndarray, add: float) -> np.ndarray: - if add == 0: - return img - - img = img + (add / 100) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/divide.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/divide.py deleted file mode 100644 index 0067471..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/divide.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import arithmetic_group - - -@arithmetic_group.register( - schema_id="chainner:image:divide", - description="Divide all channels in an image by a value.", - name="Divide", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Divide", - minimum=0.0001, - maximum=4.0, - default=1.0, - precision=4, - controls_step=0.0001, - scale="log", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def divide_node(img: np.ndarray, divide: float) -> np.ndarray: - if divide == 1.0: - return img - - img = img * (1.0 / divide) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/multiply.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/multiply.py deleted file mode 100644 index d92a9b1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/multiply.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import arithmetic_group - - -@arithmetic_group.register( - schema_id="chainner:image:multiply", - description="Multiply all channels in an image by a value.", - name="Multiply", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Multiply", - minimum=0.0, - maximum=4.0, - default=1.0, - precision=4, - controls_step=0.0001, - scale="log", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def multiply_node(img: np.ndarray, mult: float) -> np.ndarray: - if mult == 1.0: - return img - - img = img * mult - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/premultiplied_alpha.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/premultiplied_alpha.py deleted file mode 100644 index 666b29f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/arithmetic/premultiplied_alpha.py +++ /dev/null @@ -1,50 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import arithmetic_group - - -class AlphaAssociation(Enum): - PREMULTIPLY_RGB = "Straight -> Premultiplied" - UNPREMULTIPLY_RGB = "Premultiplied -> Straight" - - -@arithmetic_group.register( - schema_id="chainner:image:premultiplied_alpha", - description="Converts an RGBA Input from a Straight Alpha Association to a Premultiplied Alpha Association, or vice versa.", - name="Premultiplied Alpha", - icon="CgMathDivide", - inputs=[ - ImageInput(channels=[4]), - EnumInput( - AlphaAssociation, - label="Alpha Association Conversion", - default=AlphaAssociation.PREMULTIPLY_RGB, - option_labels={ - AlphaAssociation.PREMULTIPLY_RGB: "Straight → Premultiplied", - AlphaAssociation.UNPREMULTIPLY_RGB: "Premultiplied → Straight", - }, - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def premultiplied_alpha_node( - img: np.ndarray, alpha_association: AlphaAssociation -) -> np.ndarray: - rgb = img[..., :3] - alpha = img[..., 3] - - if alpha_association == AlphaAssociation.UNPREMULTIPLY_RGB: - rgb_divided = rgb / alpha[..., np.newaxis] - return np.concatenate((rgb_divided, alpha[..., np.newaxis]), axis=-1) - elif alpha_association == AlphaAssociation.PREMULTIPLY_RGB: - rgb_multed = rgb * alpha[..., np.newaxis] - return np.concatenate((rgb_multed, alpha[..., np.newaxis]), axis=-1) - else: - raise ValueError(f"Invalid Alpha Association State '{alpha_association}'.") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/gamma/gamma.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/gamma/gamma.py deleted file mode 100644 index 7f5f58f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/gamma/gamma.py +++ /dev/null @@ -1,40 +0,0 @@ -from __future__ import annotations - -import numpy as np -from chainner_ext import fast_gamma - -from nodes.properties.inputs import BoolInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import gamma_group - - -@gamma_group.register( - schema_id="chainner:image:gamma", - name="Gamma", - description="Adjusts the gamma of an image.", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Gamma", - minimum=0.01, - maximum=100, - default=1, - precision=4, - controls_step=0.1, - scale="log", - ), - BoolInput("Invert Gamma", default=False), - ], - outputs=[ImageOutput(shape_as=0, assume_normalized=True)], -) -def gamma_node(img: np.ndarray, gamma: float, invert_gamma: bool) -> np.ndarray: - if gamma == 1: - # noop - return img - - if invert_gamma: - gamma = 1 / gamma - - return fast_gamma(img, gamma) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/gamma/log_to_linear.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/gamma/log_to_linear.py deleted file mode 100644 index 7f9fbcc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/gamma/log_to_linear.py +++ /dev/null @@ -1,63 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import BoolInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import gamma_group - - -@gamma_group.register( - schema_id="chainner:image:log2lin", - description="Convert all channels in an image to a scene linear encoding using the Kodak Cineon logarithmic function set.", - name="Log To Linear", - icon="ImBrightnessContrast", - inputs=[ - ImageInput(), - SliderInput( - "Black", - minimum=0.0, - maximum=1023, - default=95.0, - precision=1, - controls_step=1.0, - ), - SliderInput( - "White", - minimum=0.0, - maximum=1023, - default=685.0, - precision=1, - controls_step=1.0, - ), - SliderInput( - "Gamma", - minimum=0.0001, - maximum=1.0, - default=0.6, - precision=2, - controls_step=0.0001, - scale="log", - ), - BoolInput("Invert Log to Linear", default=False).with_docs( - "When checked this will convert the input image back to the Kodak Cineon Logarithmic encoding" - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def log_to_linear_node( - img: np.ndarray, - black: float, - white: float, - gamma: float, - invert_log_to_linear: bool, -) -> np.ndarray: - offset = pow(10.0, (black - white) * 0.002 / gamma) - gain = 1.0 / (1.0 - offset) - - if not invert_log_to_linear: - img = gain * (pow(10.0, (1023.0 * img - white) * 0.002 / gamma) - offset) - else: - img = (np.log10(img / gain + offset) / (0.002 / gamma) + white) / 1023.0 - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/generate_threshold.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/generate_threshold.py deleted file mode 100644 index 5557bb9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/generate_threshold.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.utils import get_h_w_c - -from .. import threshold_group - - -class AutoThreshold(Enum): - OTSU = 0 - TRIANGLE = 1 - - -_AUTO_THRESHOLD_LABELS: dict[AutoThreshold, str] = { - AutoThreshold.OTSU: "Otsu's Method", - AutoThreshold.TRIANGLE: "Triangle Method", -} - - -@threshold_group.register( - schema_id="chainner:image:generate_threshold", - name="Generate Threshold", - description="Automatically determines an optimal threshold value for the given image.", - icon="MdShowChart", - inputs=[ - ImageInput(), - EnumInput(AutoThreshold, "Method", option_labels=_AUTO_THRESHOLD_LABELS), - ], - outputs=[ - NumberOutput("Threshold", output_type="0..100"), - ], - see_also=[ - "chainner:image:threshold", - ], -) -def generate_threshold_node(img: np.ndarray, method: AutoThreshold) -> float: - if get_h_w_c(img)[2] != 1: - # these methods need grayscale images, so we'll use the mean across all channels - img = np.mean(img, axis=-1) - - # otsu and triangle methods are only implemented for uint8 images - img = to_uint8(img, normalized=True) - - if method == AutoThreshold.OTSU: - threshold, _ = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) - elif method == AutoThreshold.TRIANGLE: - threshold, _ = cv2.threshold( - img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE - ) - - return threshold / 255 * 100 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/threshold.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/threshold.py deleted file mode 100644 index 215d18d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/threshold.py +++ /dev/null @@ -1,121 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np -from chainner_ext import binary_threshold - -from api import KeyInfo -from nodes.groups import if_enum_group -from nodes.impl.image_utils import as_2d_grayscale -from nodes.properties.inputs import BoolInput, EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import threshold_group - - -class ThresholdType(Enum): - BINARY = cv2.THRESH_BINARY - BINARY_INV = cv2.THRESH_BINARY_INV - TRUNC = cv2.THRESH_TRUNC - TO_ZERO = cv2.THRESH_TOZERO - TO_ZERO_INV = cv2.THRESH_TOZERO_INV - - -_THRESHOLD_TYPE_LABELS: dict[ThresholdType, str] = { - ThresholdType.BINARY: "Binary", - ThresholdType.BINARY_INV: "Binary (Inverted)", - ThresholdType.TRUNC: "Truncated", - ThresholdType.TO_ZERO: "To Zero", - ThresholdType.TO_ZERO_INV: "To Zero (Inverted)", -} - - -@threshold_group.register( - schema_id="chainner:image:threshold", - name="Threshold", - description="Replaces pixels based on the threshold value. If the pixel value is smaller than the threshold, it is set to 0, otherwise it is set to the maximum value.", - icon="MdShowChart", - inputs=[ - ImageInput(), - SliderInput( - "Threshold", - maximum=100, - default=50, - precision=1, - controls_step=1, - ), - EnumInput( - ThresholdType, - "Threshold Type", - default=ThresholdType.BINARY, - option_labels=_THRESHOLD_TYPE_LABELS, - ).with_id(3), - if_enum_group(3, (ThresholdType.BINARY, ThresholdType.BINARY_INV))( - SliderInput( - "Maximum Value", - maximum=100, - default=100, - precision=1, - controls_step=1, - ).with_id(2), - ), - BoolInput("Anti-aliasing", default=False) - .with_docs( - "Enables sub-pixel precision. Bilinear interpolation is used to fill in values in between pixels.", - "Conceptually, the option is equivalent to first upscaling the image by a factor of X (with linear interpolation), thresholding it, and then downscaling it by a factor of X (where X is 20 or more).", - ) - .with_id(4), - if_enum_group(4, 1)( - SliderInput("Softness", default=0, minimum=0, maximum=10) - .with_docs( - "The strength of a sub-pixel blur applied to be anti-aliased image. This can be be used to make the anti-aliasing even softer.", - "The blur is very small and higher-quality than a simple Gaussian blur. 0 means that no additional blur will be applied. 10 means that the anti-aliasing will be very soft.", - ) - .with_id(5), - ), - ], - outputs=[ - ImageOutput(shape_as=0), - ], - key_info=KeyInfo.number(1), - see_also=[ - "chainner:image:generate_threshold", - "chainner:image:threshold_adaptive", - ], -) -def threshold_node( - img: np.ndarray, - threshold: float, - thresh_type: ThresholdType, - max_value: float, - anti_aliasing: bool, - extra_smoothness: float, -) -> np.ndarray: - threshold /= 100 - max_value /= 100 - extra_smoothness /= 10 - - if not anti_aliasing: - _, result = cv2.threshold(img, threshold, max_value, thresh_type.value) - return result - - binary = binary_threshold(img, threshold, True, extra_smoothness) - if get_h_w_c(binary)[2] == 1: - binary = as_2d_grayscale(binary) - - if thresh_type == ThresholdType.BINARY_INV: - binary = 1 - binary - - if thresh_type in (ThresholdType.BINARY, ThresholdType.BINARY_INV): - if max_value < 1: - binary *= max_value - return binary - elif thresh_type == ThresholdType.TRUNC: - return binary * threshold + img * (1 - binary) - elif thresh_type == ThresholdType.TO_ZERO: - return binary * img - elif thresh_type == ThresholdType.TO_ZERO_INV: - return (1 - binary) * img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/threshold_adaptive.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/threshold_adaptive.py deleted file mode 100644 index 3049929..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_adjustment/threshold/threshold_adaptive.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import threshold_group - - -class AdaptiveThresholdType(Enum): - BINARY = cv2.THRESH_BINARY - BINARY_INV = cv2.THRESH_BINARY_INV - - -_THRESHOLD_TYPE_LABELS: dict[AdaptiveThresholdType, str] = { - AdaptiveThresholdType.BINARY: "Binary", - AdaptiveThresholdType.BINARY_INV: "Binary (Inverted)", -} - - -class AdaptiveMethod(Enum): - MEAN = cv2.ADAPTIVE_THRESH_MEAN_C - GAUSSIAN = cv2.ADAPTIVE_THRESH_GAUSSIAN_C - - -_ADAPTIVE_METHOD_LABELS: dict[AdaptiveMethod, str] = { - AdaptiveMethod.MEAN: "Mean", - AdaptiveMethod.GAUSSIAN: "Gaussian", -} - - -@threshold_group.register( - schema_id="chainner:image:threshold_adaptive", - name="Threshold (Adaptive)", - description="Similar to regular threshold, but determines the threshold for a pixel based on a small region around it.", - icon="MdAutoGraph", - inputs=[ - ImageInput(channels=1), - EnumInput( - AdaptiveThresholdType, - "Threshold Type", - default=AdaptiveThresholdType.BINARY, - option_labels=_THRESHOLD_TYPE_LABELS, - ).with_id(3), - SliderInput( - "Maximum Value", - maximum=100, - default=100, - precision=1, - controls_step=1, - ).with_id(1), - EnumInput( - AdaptiveMethod, - "Adaptive Method", - default=AdaptiveMethod.MEAN, - option_labels=_ADAPTIVE_METHOD_LABELS, - ).with_id(2), - NumberInput("Block Radius", default=1, minimum=1).with_id(4), - NumberInput( - "Constant Subtraction", - minimum=-100, - maximum=100, - default=0, - precision=1, - ) - .with_id(5) - .with_docs( - "A constant value that is subtracted from the automatically determined adaptive threshold.", - "Assuming that **Threshold Type** is *Binary*, then higher values will result in more white pixels and lower values will result in more black pixels.", - ), - ], - outputs=[ImageOutput(shape_as=0)], - limited_to_8bpc=True, -) -def threshold_adaptive_node( - img: np.ndarray, - threshold_type: AdaptiveThresholdType, - max_value: float, - adaptive_method: AdaptiveMethod, - block_radius: int, - c: float, -) -> np.ndarray: - # Adaptive threshold requires uint8 input - img = to_uint8(img, normalized=True) - - max_value = max_value / 100 * 255 - - return cv2.adaptiveThreshold( - img, - max_value, - adaptive_method.value, - threshold_type.value, - block_radius * 2 + 1, - round(c / 100 * 255), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/__init__.py deleted file mode 100644 index c6eb248..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .. import image_channel_category - -all_group = image_channel_category.add_node_group("All") -transparency_group = image_channel_category.add_node_group("Transparency") -miscellaneous_group = image_channel_category.add_node_group("Miscellaneous") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/__init__.py deleted file mode 100644 index e4abebe..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .. import all_group - -node_group = all_group diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/combine_rgba.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/combine_rgba.py deleted file mode 100644 index f806930..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/combine_rgba.py +++ /dev/null @@ -1,96 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.impl.color.color import Color -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import all_group - - -@all_group.register( - schema_id="chainner:image:combine_rgba", - name="Combine RGBA", - description=( - "Merges the given channels together and returns an RGBA image." - " All channel images must be a single channel image." - ), - icon="MdCallMerge", - inputs=[ - ImageInput("R Channel", channels=1, allow_colors=True).with_docs( - "The red channel." - ), - ImageInput("G Channel", channels=1, allow_colors=True).with_docs( - "The green channel." - ), - ImageInput("B Channel", channels=1, allow_colors=True).with_docs( - "The blue channel." - ), - ImageInput("A Channel", channels=1, allow_colors=True) - .with_docs("The alpha (transparency mask) channel.") - .make_optional(), - ], - outputs=[ - ImageOutput( - image_type=""" - def isImage(i: any) = match i { Image => true, _ => false }; - let anyImages = isImage(Input0) or isImage(Input1) or isImage(Input2) or isImage(Input3); - - if not anyImages { - error("At least one channel must be an image.") - } else { - def getWidth(i: any) = match i { Image => i.width, _ => Image.width }; - def getHeight(i: any) = match i { Image => i.height, _ => Image.height }; - - Image { - width: getWidth(Input0) & getWidth(Input1) & getWidth(Input2) & getWidth(Input3), - height: getHeight(Input0) & getHeight(Input1) & getHeight(Input2) & getHeight(Input3), - } - } - """, - channels=4, - assume_normalized=True, - ).with_never_reason("All input channels must have the same size.") - ], -) -def combine_rgba_node( - img_r: np.ndarray | Color, - img_g: np.ndarray | Color, - img_b: np.ndarray | Color, - img_a: np.ndarray | Color | None, -) -> np.ndarray: - if img_a is None: - img_a = Color.gray(1) - - start_shape = None - - # determine shape - inputs = (img_b, img_g, img_r, img_a) - for i in inputs: - if isinstance(i, np.ndarray): - start_shape = (i.shape[0], i.shape[1]) - break - - if start_shape is None: - raise ValueError( - "At least one channels must be an image, but all given channels are colors." - ) - - # check same size - for i in inputs: - if isinstance(i, np.ndarray): - assert ( - i.shape[:2] == start_shape - ), "All channel images must have the same resolution" - - channels = [ - ( - i - if isinstance(i, np.ndarray) - else i.to_image(width=start_shape[1], height=start_shape[0]) - ) - for i in inputs - ] - - return np.stack(channels, axis=2) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/merge_channels.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/merge_channels.py deleted file mode 100644 index 4e143a1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/merge_channels.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -import navi -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import all_group - - -@all_group.register( - schema_id="chainner:image:merge_channels", - name="Merge Channels", - description=( - "Merge image channels together into a ≤4 channel image. " - "Typically used for combining an image with an alpha layer." - ), - icon="MdCallMerge", - inputs=[ - ImageInput("Channel(s) A"), - ImageInput("Channel(s) B").make_optional(), - ImageInput("Channel(s) C").make_optional(), - ImageInput("Channel(s) D").make_optional(), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - size_as="Input0", - channels=""" - match ( - Input0.channels - + match Input1 { Image as i => i.channels, _ => 0 } - + match Input2 { Image as i => i.channels, _ => 0 } - + match Input3 { Image as i => i.channels, _ => 0 } - ) { - 1 => 1, - 2 | 3 => 3, - int(4..) => 4 - } - """, - ) - ) - ], - deprecated=True, -) -def merge_channels_node( - im1: np.ndarray, - im2: np.ndarray | None, - im3: np.ndarray | None, - im4: np.ndarray | None, -) -> np.ndarray: - start_shape = im1.shape[:2] - - for im in im2, im3, im4: - if im is not None: - assert ( - im.shape[:2] == start_shape - ), "All images to be merged must be the same resolution" - - imgs = [] - for img in im1, im2, im3, im4: - if img is not None: - imgs.append(img) - - for idx, img in enumerate(imgs): - if img.ndim == 2: - imgs[idx] = np.expand_dims(img, axis=2) - - img = np.concatenate(imgs, axis=2) - - # ensure output is safe number of channels - _, _, c = get_h_w_c(img) - if c == 2: - b, g = cv2.split(img) - img = cv2.merge((b, g, g)) - elif c > 4: - img = img[:, :, :4] - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/separate_rgba.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/separate_rgba.py deleted file mode 100644 index 60f8da6..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/all/separate_rgba.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import all_group - - -@all_group.register( - schema_id="chainner:image:split_channels", - name="Separate RGBA", - description=( - "Split image channels into separate channels. " - "Typically used for splitting off an alpha (transparency) layer." - ), - icon="MdCallSplit", - inputs=[ImageInput()], - outputs=[ - ImageOutput( - "R Channel", - image_type=navi.Image(size_as="Input0"), - channels=1, - assume_normalized=True, - ) - .with_docs("The red channel.") - .with_id(2), - ImageOutput( - "G Channel", - image_type=navi.Image(size_as="Input0"), - channels=1, - assume_normalized=True, - ) - .with_docs("The green channel.") - .with_id(1), - ImageOutput( - "B Channel", - image_type=navi.Image(size_as="Input0"), - channels=1, - assume_normalized=True, - ) - .with_docs("The blue channel.") - .with_id(0), - ImageOutput( - "A Channel", - image_type=navi.Image(size_as="Input0"), - channels=1, - assume_normalized=True, - ).with_docs("The alpha (transparency mask) channel."), - ], -) -def separate_rgba_node( - img: np.ndarray, -) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: - h, w, c = get_h_w_c(img) - safe_out = np.ones((h, w), dtype=np.float32) - - if img.ndim == 2: - return img, safe_out, safe_out, safe_out - - c = min(c, 4) - - out = [] - for i in range(c): - out.append(img[:, :, i]) - for _ in range(4 - c): - out.append(safe_out) - - return out[2], out[1], out[0], out[3] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/__init__.py deleted file mode 100644 index 8d3fa10..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .. import miscellaneous_group - -node_group = miscellaneous_group diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/alpha_matting.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/alpha_matting.py deleted file mode 100644 index d628424..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/alpha_matting.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np -import pymatting - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:alpha_matting", - name="Alpha Matting", - description=[ - "Uses a trimap to separate foreground from background.", - "Trimaps are a three-color image that categorize the input image into foreground (white), background (black), and undecided (gray). Alpha matting uses this information to separate the foreground from the background.", - "A trimap typically has to be created manually, but it's typically an easy task since the trimaps don't have to detailed. The only requirements are that black pixels are the background, and white pixels are the foreground. The boundary region between foreground and background can be gray.", - "The following image shows the input image (top left), its trimap (top right), the output alpha (bottom left), and the output image with a different background (bottom right):" - "![lemur_at_the_beach.png](https://github.com/pymatting/pymatting/raw/master/data/lemur/lemur_at_the_beach.png)", - ], - icon="MdContentCut", - see_also="chainner:onnx:rembg", - inputs=[ - ImageInput(channels=[3, 4]).with_docs( - "If the image has an alpha channel, it will be ignored." - ), - ImageInput("Trimap", channels=1), - SliderInput( - "Foreground Threshold", minimum=1, maximum=255, default=240 - ).with_docs( - "All pixels in the trimap brighter than this value are considered to be part of the foreground." - ), - SliderInput( - "Background Threshold", minimum=0, maximum=254, default=15 - ).with_docs( - "All pixels in the trimap darker than this value are considered to be part of the background." - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let image = Input0; - let trimap = Input1; - let fg = Input2; - let bg = Input3; - - if fg <= bg { - error("The foreground threshold must be greater than the background threshold.") - } else if image.width != trimap.width or image.height != trimap.height { - error("The image and trimap must have the same size.") - } else { - Image { width: image.width, height: image.height } - } - """, - channels=4, - ), - ], -) -def alpha_matting_node( - img: np.ndarray, - trimap: np.ndarray, - fg_threshold: int, - bg_threshold: int, -) -> np.ndarray: - assert ( - fg_threshold > bg_threshold - ), "The foreground threshold must be greater than the background threshold." - - h, w, c = get_h_w_c(img) - assert (h, w) == trimap.shape[:2], "The image and trimap must have the same size." - - # apply thresholding to trimap - trimap = np.where(trimap > fg_threshold / 255, 1, trimap) - trimap = np.where(trimap < bg_threshold / 255, 0, trimap) - trimap = trimap.astype(np.float64) - - # convert to rgb - if c == 4: - img = cv2.cvtColor(img, cv2.COLOR_BGRA2RGB) - else: - img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - img = img.astype(np.float64) - - assert img.dtype == np.float64 - assert trimap.dtype == np.float64 - alpha = pymatting.estimate_alpha_cf(img, trimap) - foreground = pymatting.estimate_foreground_ml(img, alpha) - assert isinstance(foreground, np.ndarray) - - # convert to bgr - foreground = cv2.cvtColor(foreground, cv2.COLOR_RGB2BGR) - return np.dstack((foreground, alpha)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/chroma_key.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/chroma_key.py deleted file mode 100644 index 66fa3e9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/chroma_key.py +++ /dev/null @@ -1,167 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np -import pymatting - -import navi -from nodes.groups import if_enum_group, linked_inputs_group -from nodes.impl.color.color import Color -from nodes.properties.inputs import ( - BoolInput, - ColorInput, - EnumInput, - ImageInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import miscellaneous_group - - -class KeyMethod(Enum): - BINARY = 1 - TRIMAP_MATTING = 2 - - -@miscellaneous_group.register( - schema_id="chainner:image:chroma_key", - name="Chroma Key", - description=[ - "Removes a color from an image and replaces it with transparency.", - "To set the key color, either define a constant color with the `chainner:utility:color` node or pick a color from the image with the `chainner:image:pick_color` node.", - "This nodes offers multiple strategies to key the image:", - "- **Binary**:", - " A simple binary thresholding method is used to determine the whether a pixel in the image is transparent or not. This method is mostly useful images *without anti-aliasing*.", - "- **Trimap matting**:", - " This method uses a separate threshold for foreground (FG) and background (BG) to create a trimap. Alpha matting is then performed on the trimap to create the final alpha. For more information on alpha matting, see the `chainner:image:alpha_matting` node.", - " The confusion sliders can be used to reduce the amount of pixels that are considered to be FG or BG. This is useful for anti-alised/blurred edges.", - " Since alpha matting can be slow, it is recommended to use the **Output Trimap** option. This option will use the trimap for alpha directly, without performing alpha matting. Using the fast preview, you can tweak the other parameters to generate a good trimap. Once you are satisfied with the trimap, you can disable the **Output Trimap** option to perform alpha matting.", - ], - icon="MdOutlineFormatColorFill", - inputs=[ - ImageInput(channels=3), - ColorInput(channels=3), - EnumInput(KeyMethod).with_id(2), - if_enum_group(2, KeyMethod.BINARY)( - SliderInput( - "Threshold", maximum=100, default=1, precision=1, controls_step=1 - ) - ), - if_enum_group(2, KeyMethod.TRIMAP_MATTING)( - SliderInput( - "Threshold BG", maximum=100, default=2, precision=1, controls_step=1 - ), - SliderInput( - "Threshold FG", maximum=100, default=10, precision=1, controls_step=1 - ), - linked_inputs_group( - SliderInput("Confusion BG", maximum=100, default=4, scale="log"), - SliderInput("Confusion FG", maximum=100, default=4, scale="log"), - ), - BoolInput("Output Trimap", default=False).with_docs( - "If enabled, the generated trimap will be used as alpha. No alpha matting will be performed." - ), - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image(size_as="Input0"), - channels=4, - ), - ], -) -def chroma_key_node( - img: np.ndarray, - key_color: Color, - method: KeyMethod, - binary_threshold: float, - tm_threshold_bg: float, - tm_threshold_fg: float, - tm_confusion_bg: int, - tm_confusion_fg: int, - tm_output_trimap: bool, -) -> np.ndarray: - if method == KeyMethod.BINARY: - return binary_keying(img, key_color, binary_threshold / 100) - elif method == KeyMethod.TRIMAP_MATTING: - return trimap_matting_keying( - img, - key_color, - tm_threshold_bg / 100, - tm_threshold_fg / 100, - tm_confusion_bg, - tm_confusion_fg, - tm_output_trimap, - ) - else: - raise AssertionError(f"Invalid alpha fill method {method}") - - -def binary_keying(img: np.ndarray, key_color: Color, threshold: float) -> np.ndarray: - h, w, _ = get_h_w_c(img) - - diff = np.abs(img - key_color.to_image(w, h)) - diff = np.sum(diff, axis=-1) / 3 - - alpha = np.where(diff > threshold, 1, 0).astype(np.float32) - return np.dstack([img, alpha]) - - -def trimap_matting_keying( - img: np.ndarray, - key_color: Color, - threshold_bg: float, - threshold_fg: float, - confusion_bg: int, - confusion_fg: int, - output_trimap: bool, -) -> np.ndarray: - h, w, _ = get_h_w_c(img) - - diff = np.abs(img - key_color.to_image(w, h)) - diff = np.sum(diff, axis=-1) / 3 - - # determine in and out - bg_mask = np.where(diff <= threshold_bg, 255, 0).astype(np.uint8) - fg_mask = np.where(diff > threshold_fg, 255, 0).astype(np.uint8) - if confusion_bg > 0: - element = cv2.getStructuringElement( - cv2.MORPH_ELLIPSE, (2 * confusion_bg + 1,) * 2 - ) - cv2.erode(bg_mask, element, iterations=1, dst=bg_mask) - if confusion_fg > 0: - element = cv2.getStructuringElement( - cv2.MORPH_ELLIPSE, (2 * confusion_fg + 1,) * 2 - ) - cv2.erode(fg_mask, element, iterations=1, dst=fg_mask) - - # must be float64 - trimap = np.full((h, w), 0.5, dtype=np.float64) - trimap[bg_mask > 128] = 0 - trimap[fg_mask > 128] = 1 - - if output_trimap: - return np.dstack((img, trimap.astype(np.float32))) - - # convert to rgb - img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - img = img.astype(np.float64) - - assert img.dtype == np.float64 - assert trimap.dtype == np.float64 - alpha = pymatting.estimate_alpha_cf(img, trimap) - foreground = pymatting.estimate_foreground_ml(img, alpha) - assert isinstance(foreground, np.ndarray) - - # convert to bgr - foreground = cv2.cvtColor(foreground, cv2.COLOR_RGB2BGR) - return np.dstack( - ( - foreground.astype(np.float32), - alpha.astype(np.float32), - ) - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/fill_alpha.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/fill_alpha.py deleted file mode 100644 index bc5fd66..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/misc/fill_alpha.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np -from chainner_ext import ( - fill_alpha_extend_color, - fill_alpha_fragment_blur, - fill_alpha_nearest_color, -) - -import navi -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -class AlphaFillMethod(Enum): - EXTEND_TEXTURE = 1 - EXTEND_COLOR = 2 - NEAREST_COLOR = 3 - - -@miscellaneous_group.register( - schema_id="chainner:image:fill_alpha", - name="Fill Alpha", - description="Splits the image into color and transparency, and fills the transparent pixels of an image with nearby colors.", - icon="MdOutlineFormatColorFill", - inputs=[ - ImageInput(channels=4), - EnumInput(AlphaFillMethod, label="Fill Method"), - ], - outputs=[ - ImageOutput( - "RGB", - image_type=navi.Image(size_as="Input0"), - channels=3, - ), - ImageOutput( - "Alpha", - image_type=navi.Image(size_as="Input0"), - channels=1, - ), - ], -) -def fill_alpha_node( - img: np.ndarray, method: AlphaFillMethod -) -> tuple[np.ndarray, np.ndarray]: - alpha = img[:, :, 3] - - if method == AlphaFillMethod.EXTEND_TEXTURE: - img = fill_alpha_fragment_blur( - img, - threshold=0.05, - iterations=6, - fragment_count=5, - ) - img = fill_alpha_extend_color(img, threshold=0.05, iterations=100_000) - elif method == AlphaFillMethod.EXTEND_COLOR: - img = fill_alpha_extend_color(img, threshold=0.05, iterations=100_000) - elif method == AlphaFillMethod.NEAREST_COLOR: - img = fill_alpha_nearest_color( - img, - threshold=0.05, - min_radius=100_000, - anti_aliasing=True, - ) - else: - raise AssertionError(f"Invalid alpha fill method {method}") - - return img[:, :, :3], alpha diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/__init__.py deleted file mode 100644 index 7eb8376..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .. import transparency_group - -node_group = transparency_group diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/merge_transparency.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/merge_transparency.py deleted file mode 100644 index 79a718b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/merge_transparency.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.impl.color.color import Color -from nodes.impl.image_utils import as_target_channels -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import transparency_group - - -@transparency_group.register( - schema_id="chainner:image:merge_transparency", - name="Merge Transparency", - description="Merge RGB and Alpha (transparency) image channels into 4-channel RGBA channels.", - icon="MdCallMerge", - inputs=[ - ImageInput("RGB", allow_colors=True), - ImageInput("Alpha", allow_colors=True, channels=1), - ], - outputs=[ - ImageOutput( - image_type=""" - def isImage(i: any) = match i { Image => true, _ => false }; - let anyImages = isImage(Input0) or isImage(Input1); - - if not anyImages { - error("At least one input must be an image.") - } else { - def getWidth(i: any) = match i { Image => i.width, _ => Image.width }; - def getHeight(i: any) = match i { Image => i.height, _ => Image.height }; - - Image { - width: getWidth(Input0) & getWidth(Input1), - height: getHeight(Input0) & getHeight(Input1), - } - } - """, - channels=4, - assume_normalized=True, - ).with_never_reason("RGB and Alpha must have the same size.") - ], -) -def merge_transparency_node( - rgb: np.ndarray | Color, - a: np.ndarray | Color, -) -> np.ndarray: - start_shape = None - - # determine shape - for i in rgb, a: - if isinstance(i, np.ndarray): - start_shape = (i.shape[0], i.shape[1]) - break - - if start_shape is None: - raise ValueError( - "At least one input must be an image, but both RGB and Alpha are colors." - ) - - # check same size - for i in rgb, a: - if isinstance(i, np.ndarray): - assert ( - i.shape[:2] == start_shape - ), "All channel images must have the same resolution" - - def to_image(i: np.ndarray | Color) -> np.ndarray: - if isinstance(i, np.ndarray): - return i - return i.to_image(start_shape[1], start_shape[0]) - - rgb = as_target_channels(to_image(rgb), 3, narrowing=True) - a = to_image(a) - - return np.dstack((rgb, a)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/split_transparency.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/split_transparency.py deleted file mode 100644 index d269ffa..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_channel/transparency/split_transparency.py +++ /dev/null @@ -1,49 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.image_utils import as_target_channels -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import transparency_group - - -@transparency_group.register( - schema_id="chainner:image:split_transparency", - name="Split Transparency", - description="Split image channels into RGB and Alpha (transparency) channels.", - icon="MdCallSplit", - inputs=[ImageInput(channels=[1, 3, 4])], - outputs=[ - ImageOutput( - "RGB", - image_type=navi.Image(size_as="Input0"), - channels=3, - assume_normalized=True, - ), - ImageOutput( - "Alpha", - image_type=navi.Image(size_as="Input0"), - channels=1, - assume_normalized=True, - ), - ], -) -def split_transparency_node(img: np.ndarray) -> tuple[np.ndarray, np.ndarray]: - c = get_h_w_c(img)[2] - if c == 3: - # Performance optimization: - # Subsequent operations will be faster since the underlying array will - # be contiguous in memory. The speed up can anything from nothing to - # 5x faster depending on the operation. - return img, np.ones(img.shape[:2], dtype=img.dtype) - - img = as_target_channels(img, 4) - - rgb = img[:, :, :3] - alpha = img[:, :, 3] - - return rgb, alpha diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/__init__.py deleted file mode 100644 index 6e914c2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from .. import image_dimensions_category - -padding_group = image_dimensions_category.add_node_group("Padding") -crop_group = image_dimensions_category.add_node_group("Crop") -resize_group = image_dimensions_category.add_node_group("Resize") -utility_group = image_dimensions_category.add_node_group("Utility") - -resize_group.order = [ - "chainner:image:resize", - "chainner:image:resize_to_side", -] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/create_border.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/create_border.py deleted file mode 100644 index 56e2d23..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/create_border.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.groups import if_enum_group -from nodes.impl.color.color import Color -from nodes.impl.image_utils import BorderType, create_border -from nodes.properties.inputs import BorderInput, ColorInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import Padding - -from .. import border_group - - -@border_group.register( - schema_id="chainner:image:create_border", - name="Create Border", - description="Creates a border around the image.", - icon="BsBorderOuter", - inputs=[ - ImageInput(), - BorderInput().with_id(1), - if_enum_group(1, BorderType.CUSTOM_COLOR)( - ColorInput().with_id(3), - ), - NumberInput("Amount", unit="px").with_id(2), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input0.width + Input2 * 2", - height="Input0.height + Input2 * 2", - channels="BorderType::getOutputChannels(Input1, Input0.channels, Input3)", - ), - assume_normalized=True, - ) - ], -) -def create_border_node( - img: np.ndarray, - border_type: BorderType, - color: Color, - amount: int, -) -> np.ndarray: - return create_border(img, border_type, Padding.all(amount), color=color) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/create_edges.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/create_edges.py deleted file mode 100644 index 2fa4c57..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/create_edges.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.groups import if_enum_group -from nodes.impl.color.color import Color -from nodes.impl.image_utils import BorderType, create_border -from nodes.properties.inputs import BorderInput, ColorInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import Padding - -from .. import border_group - - -@border_group.register( - schema_id="chainner:image:create_edges", - name="Create Edges", - description="Creates a border around the image with different values per edge.", - icon="BsBorderOuter", - inputs=[ - ImageInput(), - BorderInput().with_id(1), - if_enum_group(1, BorderType.CUSTOM_COLOR)( - ColorInput().with_id(6), - ), - NumberInput("Top", unit="px").with_id(2), - NumberInput("Left", unit="px").with_id(3), - NumberInput("Right", unit="px").with_id(4), - NumberInput("Bottom", unit="px").with_id(5), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input0.width + Input3 + Input4", - height="Input0.height + Input2 + Input5", - channels="BorderType::getOutputChannels(Input1, Input0.channels, Input6)", - ), - assume_normalized=True, - ) - ], -) -def create_edges_node( - img: np.ndarray, - border_type: BorderType, - color: Color, - top: int, - left: int, - right: int, - bottom: int, -) -> np.ndarray: - return create_border( - img, border_type, Padding(top, right, bottom, left), color=color - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/pad.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/pad.py deleted file mode 100644 index eafe2b5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/border/pad.py +++ /dev/null @@ -1,130 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from api import KeyInfo -from nodes.groups import if_enum_group -from nodes.impl.color.color import Color -from nodes.impl.image_utils import BorderType, create_border -from nodes.properties.inputs import ( - BorderInput, - ColorInput, - EnumInput, - ImageInput, - NumberInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import Padding, get_h_w_c - -from .. import padding_group - - -class BorderMode(Enum): - BORDER = 0 - EDGES = 1 - OFFSETS = 2 - - -@padding_group.register( - schema_id="chainner:image:pad", - name="Pad", - description=[ - "Adds padding to an image.", - "This node can be used to add a border or edges to an image.", - ], - icon="BsBorderOuter", - inputs=[ - ImageInput(), - BorderInput().with_id(1), - if_enum_group(1, BorderType.CUSTOM_COLOR)( - ColorInput().with_id(2), - ), - EnumInput( - BorderMode, default=BorderMode.BORDER, preferred_style="tabs" - ).with_id(3), - if_enum_group(3, BorderMode.BORDER)( - NumberInput("Amount", unit="px").with_id(4), - ), - if_enum_group(3, (BorderMode.EDGES, BorderMode.OFFSETS))( - NumberInput("Left", unit="px").with_id(5), - NumberInput("Top", unit="px").with_id(6), - ), - if_enum_group(3, BorderMode.EDGES)( - NumberInput("Right", unit="px").with_id(7), - NumberInput("Bottom", unit="px").with_id(8), - ), - if_enum_group(3, BorderMode.OFFSETS)( - NumberInput("Width", unit="px", minimum=1, default=100).with_id(9), - NumberInput("Height", unit="px", minimum=1, default=100).with_id(10), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let i = Input0; - - let border = Input1; - let color = Input2; - - let amount = Input4; - let left = Input5; - let top = Input6; - let right = Input7; - let bottom = Input8; - let width = Input9; - let height = Input10; - - let size = match Input3 { - BorderMode::Border => Image { - width: i.width + amount * 2, - height: i.height + amount * 2, - }, - BorderMode::Edges => Image { - width: i.width + left + right, - height: i.height + top + bottom, - }, - BorderMode::Offsets => Image { - width, - height, - }, - }; - - size & Image { channels: BorderType::getOutputChannels(border, i.channels, color) } - """, - assume_normalized=True, - ) - ], - key_info=KeyInfo.enum(3), -) -def pad_node( - img: np.ndarray, - border_type: BorderType, - color: Color, - border_mode: BorderMode, - amount: int, - left: int, - top: int, - right: int, - bottom: int, - width: int, - height: int, -) -> np.ndarray: - if border_mode == BorderMode.BORDER: - return create_border(img, border_type, Padding.all(amount), color=color) - elif border_mode == BorderMode.EDGES: - return create_border( - img, border_type, Padding(top, right, bottom, left), color=color - ) - elif border_mode == BorderMode.OFFSETS: - h, w, _ = get_h_w_c(img) - r = width - left - w - b = height - top - h - padded = create_border( - img, border_type, Padding(top, max(0, r), max(0, b), left), color=color - ) - if r < 0 or b < 0: - # copy, so we don't keep a reference to the underlying array - padded = padded[:height, :width, ...].copy() - return padded diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop.py deleted file mode 100644 index 53d4430..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop.py +++ /dev/null @@ -1,116 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from api import KeyInfo -from nodes.groups import if_enum_group -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import crop_group - - -class CropMode(Enum): - BORDER = 0 - EDGES = 1 - OFFSETS = 2 - - -@crop_group.register( - schema_id="chainner:image:crop", - name="Crop", - description="Crop an image.", - icon="MdCrop", - inputs=[ - ImageInput(), - EnumInput(CropMode, default=CropMode.BORDER, preferred_style="tabs").with_id(1), - if_enum_group(1, CropMode.BORDER)( - NumberInput("Amount", unit="px").with_id(2), - ), - if_enum_group(1, (CropMode.EDGES, CropMode.OFFSETS))( - NumberInput("Left", unit="px").with_id(4), - NumberInput("Top", unit="px").with_id(3), - ), - if_enum_group(1, CropMode.EDGES)( - NumberInput("Right", unit="px").with_id(6), - NumberInput("Bottom", unit="px").with_id(5), - ), - if_enum_group(1, CropMode.OFFSETS)( - NumberInput("Width", unit="px", minimum=1, default=100).with_id(8), - NumberInput("Height", unit="px", minimum=1, default=100).with_id(7), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let i = Input0; - - let amount = Input2; - let top = Input3; - let left = Input4; - let bottom = Input5; - let right = Input6; - let height = Input7; - let width = Input8; - - let size = match Input1 { - CropMode::Border => Image { - width: (i.width - amount * 2) & int(1..), - height: (i.height - amount * 2) & int(1..), - }, - CropMode::Edges => Image { - width: (i.width - (left + right)) & int(1..), - height: (i.height - (top + bottom)) & int(1..), - }, - CropMode::Offsets => Image { - width: min(width, i.width - left) & int(1..), - height: min(height, i.height - top) & int(1..), - }, - }; - - size & Image { channels: i.channels } - """, - assume_normalized=True, - ).with_never_reason( - "The cropped area would result in an image with no width or no height." - ) - ], - key_info=KeyInfo.enum(1), -) -def crop_node( - img: np.ndarray, - mode: CropMode, - amount: int, - left: int, - top: int, - right: int, - bottom: int, - width: int, - height: int, -) -> np.ndarray: - h, w, _ = get_h_w_c(img) - - if mode == CropMode.BORDER: - if amount == 0: - return img - - assert 2 * amount < h, "Cropped area would result in an image with no height" - assert 2 * amount < w, "Cropped area would result in an image with no width" - - return img[amount : h - amount, amount : w - amount] - elif mode == CropMode.EDGES: - if top == bottom == left == right == 0: - return img - - assert top + bottom < h, "Cropped area would result in an image with no height" - assert left + right < w, "Cropped area would result in an image with no width" - - return img[top : h - bottom, left : w - right] - elif mode == CropMode.OFFSETS: - assert top < h, "Cropped area would result in an image with no height" - assert left < w, "Cropped area would result in an image with no width" - - return img[top : top + height, left : left + width] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop_border.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop_border.py deleted file mode 100644 index 358246b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop_border.py +++ /dev/null @@ -1,190 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from enum import Enum - -import numpy as np - -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import Padding, Region, get_h_w_c - -from .. import crop_group - - -class SelectMode(Enum): - ALL_SECTIONS = 1 - CENTER_SECTION = 2 - LARGEST_SECTION = 3 - - -@crop_group.register( - schema_id="chainner:image:remove_border", - name="Crop Border", - description=[ - "Remove the border around the content of an image. The border is assumed to have an approximately constant color. This node can also be used to crop images with an approximately constant background color.", - "The color of the border is automatically determined by the median color of the pixels of a 1px border around the image. The *Tolerance* option can be used to adjust the sensitivity of the color matching. A tolerance of 0% means that a pixel must match the border color exactly, while a tolerance of 5% (default) give a bit of leeway to handle e.g. slight compression artifacts.", - ], - icon="MdCrop", - inputs=[ - ImageInput(), - SliderInput( - "Tolerance", - default=5, - minimum=0, - maximum=30, - controls_step=1, - precision=1, - unit="%", - ), - EnumInput( - SelectMode, "Select", default=SelectMode.ALL_SECTIONS, label_style="inline" - ).with_docs( - "Determines which sections of the image will be selected as the output image.", - "To support removing the border of images with captions (or other information in the border), the *Select* option can be used to select which content section of the image will be returned by the node. The available options are as follows:", - "- All Sections: A single crop containing all sections will be returned.\n" - "- Center Section: The section closest to the center of the image will be returned.\n" - "- Largest Section: The largest section will be returned.", - "So if the image has a border and a caption, *All* will return the inner image + caption, *Center*/*Largest* will return only the inner image (assuming the caption isn't larger than the inner image).", - ), - NumberInput("Padding", default=0, minimum=0, maximum=1000, unit="px").with_docs( - "Additional padding around the selected section.", - "This can be used to avoid cutting off parts of the image that are close to the border.", - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let pad = Input3; - - Image { - width: min(max(uint, 1 + 2 * pad), Input0.width), - height: min(max(uint, 1 + 2 * pad), Input0.height), - channels: Input0.channels, - } - """, - assume_normalized=True, - ), - ], -) -def crop_border_node( - img: np.ndarray, tolerance: float, select: SelectMode, padding: int -) -> np.ndarray: - tolerance /= 100 - - h, w, c = get_h_w_c(img) - - # find the border color of the border - border_color = get_border_color(img) - - # figure out which pixels are likely part of the border - diff: np.ndarray = np.abs(img - border_color) - if c > 1: - # make grayscale - diff = np.mean(diff, axis=-1) - is_content = diff > tolerance - - # get crop region crop bounds - crop = get_crop_region(is_content, select) - crop = crop.add_padding(Padding.all(padding)) - crop = crop.intersect(Region(0, 0, w, h)) - - return crop.read_from(img) - - -def get_crop_region(is_content: np.ndarray, select: SelectMode) -> Region: - assert is_content.ndim == 2 - - # 1. crop horizontally - is_content_horizontal = np.any(is_content, axis=0) - section_w = get_inner_section(is_content_horizontal, select) - is_content = is_content[:, section_w.start : section_w.end] - - # 2. crop vertically - is_content_vertical = np.any(is_content, axis=1) - section_h = get_inner_section(is_content_vertical, select) - is_content = is_content[section_h.start : section_h.end, :] - - crop = Region( - section_w.start, - section_h.start, - section_w.length, - section_h.length, - ) - - if select != SelectMode.ALL_SECTIONS: - # 3. crop horizontally again to remove any remaining border - is_content_horizontal = np.any(is_content, axis=0) - section_w = get_inner_section(is_content_horizontal, SelectMode.ALL_SECTIONS) - crop = Region( - crop.x + section_w.start, - crop.y, - section_w.length, - crop.height, - ) - - return crop - - -def get_border_color(img: np.ndarray): - """ - Returns the median color in the 1px border of the image. - """ - # Get the 1px border of the image - top = img[0, :, ...] - bottom = img[-1, :, ...] - left = img[:, 0, ...] - right = img[:, -1, ...] - border = np.concatenate((top, bottom, left, right), axis=0) - # Get the median color - return np.median(border, axis=0) - - -@dataclass(frozen=True) -class Section: - start: int - end: int - - @property - def length(self) -> int: - return self.end - self.start - - def union(self, other: Section) -> Section: - return Section(min(self.start, other.start), max(self.end, other.end)) - - def distance_to(self, index: int) -> int: - if index < self.start: - return self.start - index - if index >= self.end: - return index - self.end - return 0 - - -def get_inner_section(is_content: np.ndarray, select: SelectMode) -> Section: - assert is_content.ndim == 1 - size = len(is_content) - - # find all content sections in the image - sections: list[Section] = [] - start = None - for i in range(size): - if not is_content[i]: - if start is not None: - sections.append(Section(start, i)) - start = None - elif start is None: - start = i - if start is not None: - sections.append(Section(start, size)) - start = None - if len(sections) == 0: - return Section(0, size) - - # select the relevant section - if select == SelectMode.ALL_SECTIONS: - return sections[0].union(sections[-1]) - if select == SelectMode.CENTER_SECTION: - distances = [section.distance_to(size // 2) for section in sections] - return sections[np.argmin(distances)] - if select == SelectMode.LARGEST_SECTION: - return max(sections, key=lambda section: section.length) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop_to_content.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop_to_content.py deleted file mode 100644 index ecedcc3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/crop/crop_to_content.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import crop_group - - -@crop_group.register( - schema_id="chainner:image:crop_content", - name="Crop to Content", - description=( - "Crop an image to the boundaries of the visible image content, " - "removing borders at or below the given opacity threshold." - ), - icon="MdCrop", - inputs=[ - ImageInput(), - SliderInput( - "Threshold", - precision=1, - controls_step=1, - slider_step=1, - default=0, - ), - ], - outputs=[ - ImageOutput( - image_type=""" - match Input0.channels { - ..3 => Input0, - _ => Image { - width: min(uint, Input0.width) & 1.., - height: min(uint, Input0.height) & 1.., - channels: Input0.channels - } - } - """, - assume_normalized=True, - ) - ], -) -def crop_to_content_node(img: np.ndarray, thresh_val: float) -> np.ndarray: - c = get_h_w_c(img)[2] - if c < 4: - return img - - # Threshold value 100 guarantees an empty image, so make sure the max - # is just below that. - thresh_val = min(thresh_val / 100, 0.99999) - - # Valid alpha is greater than threshold, else impossible to crop 0 alpha only - alpha = img[:, :, 3] - r = np.any(alpha > thresh_val, 1) - if r.any(): - h, w, _ = get_h_w_c(img) - c = np.any(alpha > thresh_val, 0) - imgout = np.copy(img)[ - r.argmax() : h - r[::-1].argmax(), c.argmax() : w - c[::-1].argmax() - ] - else: - raise RuntimeError("Crop results in empty image.") - - return imgout diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize.py deleted file mode 100644 index fb5379e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize.py +++ /dev/null @@ -1,134 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from api import KeyInfo -from nodes.groups import Condition, if_enum_group, if_group -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - ImageInput, - NumberInput, - ResizeFilterInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c, round_half_up - -from .. import resize_group - - -class ImageResizeMode(Enum): - PERCENTAGE = 0 - ABSOLUTE = 1 - - -@resize_group.register( - schema_id="chainner:image:resize", - name="Resize", - description=[ - "Resize an image by a percent scale factor or absolute dimensions.", - "Auto uses box for downsampling and lanczos for upsampling.", - ], - icon="MdOutlinePhotoSizeSelectLarge", - inputs=[ - ImageInput(), - EnumInput( - ImageResizeMode, default=ImageResizeMode.PERCENTAGE, preferred_style="tabs" - ).with_id(1), - if_enum_group(1, ImageResizeMode.PERCENTAGE)( - NumberInput( - "Percentage", - precision=4, - controls_step=25.0, - default=100.0, - unit="%", - label_style="hidden", - ).with_id(2), - ), - if_enum_group(1, ImageResizeMode.ABSOLUTE)( - NumberInput("Width", minimum=1, default=1, unit="px").with_id(3), - NumberInput("Height", minimum=1, default=1, unit="px").with_id(4), - ), - ResizeFilterInput().with_id(5), - if_group(Condition.type(0, "Image { channels: 4 } "))( - BoolInput("Separate Alpha", default=False) - .with_docs( - "Resize alpha separately from color. Enable this option if the alpha channel of the image is **not** transparency.", - "To resize images with transparency correctly, the alpha channels must be multiplied with the color channels before resizing. While this will produce correct color values, it will also set the color of fully transparent pixels to black. This is an issue if the alpha channel isn't transparency. E.g. games often use the alpha channel of textures for other purposes, such as height maps or edge maps. For such images, the alpha channel has to be resized separately or else it will corrupt the color channels.", - "For images where the alpha channel is transparency (most transparent images), this option should be **disabled**.", - hint=True, - ) - .with_id(6) - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let i = Input0; - let mode = Input1; - - let scale = Input2; - let width = Input3; - let height = Input4; - - match mode { - ImageResizeMode::Percentage => Image { - width: max(1, int & round(i.width * scale / 100)), - height: max(1, int & round(i.height * scale / 100)), - channels: i.channels, - }, - ImageResizeMode::Absolute => Image { - width, - height, - channels: i.channels, - }, - } - """, - assume_normalized=True, - ) - ], - key_info=KeyInfo.type( - """ - let mode = Input1; - - let scale = Input2; - let width = Input3; - let height = Input4; - - match mode { - ImageResizeMode::Percentage => string::concat(toString(scale), "%"), - ImageResizeMode::Absolute => string::concat(toString(width), "x", toString(height)), - } - """ - ), -) -def resize_node( - img: np.ndarray, - mode: ImageResizeMode, - scale: float, - width: int, - height: int, - filter: ResizeFilter, - separate_alpha: bool, -) -> np.ndarray: - h, w, _ = get_h_w_c(img) - - out_dims: tuple[int, int] - if mode == ImageResizeMode.PERCENTAGE: - out_dims = ( - max(round_half_up(w * (scale / 100)), 1), - max(round_half_up(h * (scale / 100)), 1), - ) - else: - out_dims = (width, height) - - return resize( - img, - out_dims, - filter, - separate_alpha=separate_alpha, - gamma_correction=False, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_factor.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_factor.py deleted file mode 100644 index b181800..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_factor.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import annotations - -import numpy as np -from sanic.log import logger - -import navi -from nodes.impl.pil_utils import InterpolationMethod, resize -from nodes.properties.inputs import ImageInput, InterpolationInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c, round_half_up - -from .. import resize_group - - -@resize_group.register( - schema_id="chainner:image:resize_factor", - name="Resize (Factor)", - description=( - "Resize an image by a percent scale factor. " - "Auto uses box for downsampling and lanczos for upsampling." - ), - icon="MdOutlinePhotoSizeSelectLarge", - inputs=[ - ImageInput(), - NumberInput( - "Scale Factor", - precision=4, - controls_step=25.0, - default=100.0, - unit="%", - ), - InterpolationInput(), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="max(1, int & round(Input0.width * Input1 / 100))", - height="max(1, int & round(Input0.height * Input1 / 100))", - channels_as="Input0", - ), - assume_normalized=True, - ) - ], - limited_to_8bpc=True, -) -def resize_factor_node( - img: np.ndarray, scale: float, interpolation: InterpolationMethod -) -> np.ndarray: - """Takes an image and resizes it""" - - logger.debug(f"Resizing image by {scale} via {interpolation}") - - h, w, _ = get_h_w_c(img) - out_dims = ( - max(round_half_up(w * (scale / 100)), 1), - max(round_half_up(h * (scale / 100)), 1), - ) - - return resize(img, out_dims, interpolation) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_pixel_art.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_pixel_art.py deleted file mode 100644 index 301789a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_pixel_art.py +++ /dev/null @@ -1,109 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np -from chainner_ext import pixel_art_upscale - -from nodes.properties.inputs import ( - EnumInput, - ImageInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import resize_group - - -class ResizeAlgorithm(Enum): - ADV_MANE_2X = "adv_mame2x" - ADV_MANE_3X = "adv_mame3x" - ADV_MANE_4X = "adv_mame4x" - EAGLE_2X = "eagle2x" - EAGLE_3X = "eagle3x" - SUPER_EAGLE_2X = "super_eagle2x" - SAI_2X = "sai2x" - SUPER_SAI_2X = "super_sai2x" - HQ2X = "hqx2x" - HQ3X = "hqx3x" - HQ4X = "hqx4x" - - @property - def algorithm(self) -> str: - return self.value[:-2] - - @property - def scale(self) -> int: - if self in (ResizeAlgorithm.ADV_MANE_4X, ResizeAlgorithm.HQ4X): - return 4 - if self in ( - ResizeAlgorithm.ADV_MANE_3X, - ResizeAlgorithm.EAGLE_3X, - ResizeAlgorithm.HQ3X, - ): - return 3 - return 2 - - -ALGORITHM_LABEL: dict[ResizeAlgorithm, str] = { - ResizeAlgorithm.ADV_MANE_2X: "EXP/AdvMAME 2x", - ResizeAlgorithm.ADV_MANE_3X: "EXP/AdvMAME 3x", - ResizeAlgorithm.ADV_MANE_4X: "EXP/AdvMAME 4x", - ResizeAlgorithm.EAGLE_2X: "Eagle 2x", - ResizeAlgorithm.EAGLE_3X: "Eagle 3x", - ResizeAlgorithm.SUPER_EAGLE_2X: "Super Eagle 2x", - ResizeAlgorithm.SAI_2X: "SaI 2x", - ResizeAlgorithm.SUPER_SAI_2X: "Super SaI 2x", - ResizeAlgorithm.HQ2X: "HQ 2x", - ResizeAlgorithm.HQ3X: "HQ 3x", - ResizeAlgorithm.HQ4X: "HQ 4x", -} - - -@resize_group.register( - schema_id="chainner:image:resize_pixel_art", - name="Resize Pixel Art", - description=[ - "Upscales pixel art images using a variety of algorithms.", - "An overview of the algorithms can be found [here](https://en.wikipedia.org/w/index.php?title=Pixel-art_scaling_algorithms&oldid=1181447123).", - ], - icon="MdOutlinePhotoSizeSelectLarge", - inputs=[ - ImageInput(channels=[1, 3, 4]), - EnumInput( - ResizeAlgorithm, - label="Method", - default=ResizeAlgorithm.HQ2X, - option_labels=ALGORITHM_LABEL, - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let scale: uint = match Input1 { - ResizeAlgorithm::AdvMane2X => 2, - ResizeAlgorithm::AdvMane3X => 3, - ResizeAlgorithm::AdvMane4X => 4, - ResizeAlgorithm::Eagle2X => 2, - ResizeAlgorithm::Eagle3X => 3, - ResizeAlgorithm::SuperEagle2X => 2, - ResizeAlgorithm::Sai2X => 2, - ResizeAlgorithm::SuperSai2X => 2, - ResizeAlgorithm::Hq2X => 2, - ResizeAlgorithm::Hq3X => 3, - ResizeAlgorithm::Hq4X => 4, - }; - - Image { - width: Input0.width * scale, - height: Input0.height * scale, - channels: Input0.channels - } - """, - ) - ], -) -def resize_pixel_art_node( - img: np.ndarray, - algorithm: ResizeAlgorithm, -) -> np.ndarray: - return pixel_art_upscale(img, algorithm.algorithm, algorithm.scale) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_resolution.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_resolution.py deleted file mode 100644 index 5e9b75b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_resolution.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -import numpy as np -from sanic.log import logger - -import navi -from nodes.impl.pil_utils import InterpolationMethod, resize -from nodes.properties.inputs import ImageInput, InterpolationInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import resize_group - - -@resize_group.register( - schema_id="chainner:image:resize_resolution", - name="Resize (Resolution)", - description=( - "Resize an image to an exact resolution. " - "Auto uses box for downsampling and lanczos for upsampling." - ), - icon="MdOutlinePhotoSizeSelectLarge", - inputs=[ - ImageInput(), - NumberInput("Width", minimum=1, default=1, unit="px"), - NumberInput("Height", minimum=1, default=1, unit="px"), - InterpolationInput(), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input1", - height="Input2", - channels="Input0.channels", - ), - assume_normalized=True, - ) - ], - limited_to_8bpc=True, -) -def resize_resolution_node( - img: np.ndarray, - width: int, - height: int, - interpolation: InterpolationMethod, -) -> np.ndarray: - """Takes an image and resizes it""" - - logger.debug(f"Resizing image to {width}x{height} via {interpolation}") - - out_dims = (width, height) - - return resize(img, out_dims, interpolation) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_to_side.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_to_side.py deleted file mode 100644 index 3ba744a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/resize_to_side.py +++ /dev/null @@ -1,179 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import ( - EnumInput, - ImageInput, - NumberInput, - ResizeFilterInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c, round_half_up - -from .. import resize_group - - -class SideSelection(Enum): - WIDTH = "width" - HEIGHT = "height" - SHORTER_SIDE = "shorter side" - LONGER_SIDE = "longer side" - - -class ResizeCondition(Enum): - BOTH = "both" - UPSCALE = "upscale" - DOWNSCALE = "downscale" - - -def resize_to_side_conditional( - w: int, h: int, target: int, side: SideSelection, condition: ResizeCondition -) -> tuple[int, int]: - def compare_conditions(b: int) -> bool: - if condition == ResizeCondition.BOTH: - return False - if condition == ResizeCondition.DOWNSCALE: - return target > b - elif condition == ResizeCondition.UPSCALE: - return target < b - else: - raise RuntimeError(f"Unknown condition {condition}") - - if side == SideSelection.WIDTH: - if compare_conditions(w): - w_new = w - h_new = h - else: - w_new = target - h_new = max(round_half_up((target / w) * h), 1) - - elif side == SideSelection.HEIGHT: - if compare_conditions(h): - w_new = w - h_new = h - else: - w_new = max(round_half_up((target / h) * w), 1) - h_new = target - - elif side == SideSelection.SHORTER_SIDE: - if compare_conditions(min(h, w)): - w_new = w - h_new = h - else: - w_new = max(round_half_up((target / min(h, w)) * w), 1) - h_new = max(round_half_up((target / min(h, w)) * h), 1) - - elif side == SideSelection.LONGER_SIDE: - if compare_conditions(max(h, w)): - w_new = w - h_new = h - else: - w_new = max(round_half_up((target / max(h, w)) * w), 1) - h_new = max(round_half_up((target / max(h, w)) * h), 1) - - else: - raise RuntimeError(f"Unknown side selection {side}") - - return w_new, h_new - - -@resize_group.register( - schema_id="chainner:image:resize_to_side", - name="Resize To Side", - description=( - "Resize an image to a given side length while keeping aspect ratio. " - "Auto uses box for downsampling and lanczos for upsampling." - ), - icon="MdOutlinePhotoSizeSelectLarge", - inputs=[ - ImageInput(), - NumberInput( - "Size Target", - default=2160, - minimum=1, - unit="px", - ), - EnumInput(SideSelection, label="Resize To"), - EnumInput( - ResizeCondition, - option_labels={ - ResizeCondition.BOTH: "Upscale And Downscale", - ResizeCondition.UPSCALE: "Upscale Only", - ResizeCondition.DOWNSCALE: "Downscale Only", - }, - ).with_id(4), - ResizeFilterInput().with_id(3), - ], - outputs=[ - ImageOutput( - image_type=""" - struct Size { width: uint, height: uint } - - let w = Input0.width; - let h = Input0.height; - let target = Input1; - let side = Input2; - let condition = Input4; - - def compareCondition(b: uint): bool { - match condition { - ResizeCondition::Both => false, - ResizeCondition::Downscale => target > b, - ResizeCondition::Upscale => target < b - } - } - - let same = Size { width: w, height: h }; - - let outSize = match side { - SideSelection::Width => if compareCondition(w) { same } else { - Size { - width: target, - height: max(round((target / w) * h), 1) - } - }, - SideSelection::Height => if compareCondition(h) { same } else { - Size { - width: max(round((target / h) * w), 1), - height: target - } - }, - SideSelection::ShorterSide => if compareCondition(min(h, w)) { same } else { - Size { - width: max(round((target / min(h, w)) * w), 1), - height: max(round((target / min(h, w)) * h), 1) - } - }, - SideSelection::LongerSide => if compareCondition(max(h, w)) { same } else { - Size { - width: max(round((target / max(h, w)) * w), 1), - height: max(round((target / max(h, w)) * h), 1) - } - }, - }; - - Image { - width: outSize.width, - height: outSize.height, - channels: Input0.channels - } - """, - assume_normalized=True, - ) - ], -) -def resize_to_side_node( - img: np.ndarray, - target: int, - side: SideSelection, - condition: ResizeCondition, - filter: ResizeFilter, -) -> np.ndarray: - h, w, _ = get_h_w_c(img) - out_dims = resize_to_side_conditional(w, h, target, side, condition) - - return resize(img, out_dims, filter) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/tile_fill.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/tile_fill.py deleted file mode 100644 index db800c8..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/resize/tile_fill.py +++ /dev/null @@ -1,38 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.tile import TileMode, tile_image -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import resize_group - - -@resize_group.register( - schema_id="chainner:image:tile_fill", - name="Tile Fill", - description="Tiles an image to an exact resolution.", - icon="MdWindow", - inputs=[ - ImageInput(), - NumberInput("Width", minimum=1, default=1, unit="px"), - NumberInput("Height", minimum=1, default=1, unit="px"), - EnumInput(TileMode), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input1", - height="Input2", - channels="Input0.channels", - ), - assume_normalized=True, - ) - ], -) -def tile_fill_node( - img: np.ndarray, width: int, height: int, tile_mode: TileMode -) -> np.ndarray: - return tile_image(img, width, height, tile_mode) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/utility/get_bounding_box.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/utility/get_bounding_box.py deleted file mode 100644 index cd0ac2c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/utility/get_bounding_box.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.utils import get_h_w_c - -from .. import utility_group - - -@utility_group.register( - schema_id="chainner:image:get_bbox", - name="Get Bounding Box", - description="Gets a bounding box (X, Y, Height, and Width) of the white area of a mask.", - icon="BsBoundingBox", - inputs=[ - ImageInput(channels=1), - SliderInput( - "Threshold", - precision=1, - minimum=0, - maximum=100, - controls_step=1, - slider_step=1, - default=0, - ), - ], - outputs=[ - NumberOutput("X", output_type="min(uint, Input0.width - 1) & 0.."), - NumberOutput("Y", output_type="min(uint, Input0.height - 1) & 0.."), - NumberOutput("Width", output_type="min(uint, Input0.width) & 1.."), - NumberOutput("Height", output_type="min(uint, Input0.height) & 1.."), - ], -) -def get_bounding_box_node( - img: np.ndarray, - thresh_val: float, -) -> tuple[int, int, int, int]: - # Threshold value 100 guarantees an empty image, so make sure the max - # is just below that. - thresh = min(thresh_val / 100, 0.99999) - h, w, _ = get_h_w_c(img) - - r = np.any(img > thresh, 1) - c = np.any(img > thresh, 0) - if not r.any(): - raise RuntimeError("Resulting bounding box is empty.") - - x, y = c.argmax(), r.argmax() - return int(x), int(y), int(w - x - c[::-1].argmax()), int(h - y - r[::-1].argmax()) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/utility/get_dimensions.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/utility/get_dimensions.py deleted file mode 100644 index 0485411..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_dimension/utility/get_dimensions.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.utils import get_h_w_c - -from .. import utility_group - - -@utility_group.register( - schema_id="chainner:image:get_dims", - name="Get Dimensions", - description=("Get the Height, Width, and number of Channels from an image."), - icon="BsRulers", - inputs=[ - ImageInput(), - ], - outputs=[ - NumberOutput("Width", output_type="Input0.width"), - NumberOutput("Height", output_type="Input0.height"), - NumberOutput("Channels", output_type="Input0.channels"), - ], -) -def get_dimensions_node( - img: np.ndarray, -) -> tuple[int, int, int]: - h, w, c = get_h_w_c(img) - return w, h, c diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/__init__.py deleted file mode 100644 index 44386ab..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from .. import image_filter_category - -blur_group = image_filter_category.add_node_group("Blur") -sharpen_group = image_filter_category.add_node_group("Sharpen") -noise_group = image_filter_category.add_node_group("Noise") -quantize_group = image_filter_category.add_node_group("Quantize") -correction_group = image_filter_category.add_node_group("Correction") -miscellaneous_group = image_filter_category.add_node_group("Miscellaneous") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/box_blur.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/box_blur.py deleted file mode 100644 index 90d80eb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/box_blur.py +++ /dev/null @@ -1,137 +0,0 @@ -from __future__ import annotations - -import math -from math import ceil - -import cv2 -import numpy as np - -from nodes.groups import linked_inputs_group -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import blur_group - - -def get_kernel_1d(radius: float) -> np.ndarray: - kernel = np.ones(ceil(radius) * 2 + 1, np.float32) - - d = radius % 1 - if d != 0: - kernel[0] *= d - kernel[-1] *= d - - # normalize - kernel /= np.sum(kernel) - - return kernel - - -def get_kernel_2d(radius_x: float, radius_y: float) -> np.ndarray: - # Create kernel of dims h * w, rounded up to the closest odd integer - kernel = np.ones((ceil(radius_y) * 2 + 1, ceil(radius_x) * 2 + 1), np.float32) / ( - (2 * radius_y + 1) * (2 * radius_x + 1) - ) - - # Modify edges of kernel by fractional amount if kernel size (2r+1) is not odd integer - x_d = radius_x % 1 - y_d = radius_y % 1 - if y_d != 0: - kernel[0, :] *= y_d - kernel[-1, :] *= y_d - if x_d != 0: - kernel[:, 0] *= x_d - kernel[:, -1] *= x_d - - return kernel - - -@blur_group.register( - schema_id="chainner:image:blur", - name="Box Blur", - description="Apply box/average blur to an image.", - icon="MdBlurOn", - inputs=[ - ImageInput(), - linked_inputs_group( - SliderInput( - "Radius X", - minimum=0, - maximum=1000, - default=1, - precision=1, - controls_step=1, - slider_step=0.1, - scale="log", - ), - SliderInput( - "Radius Y", - minimum=0, - maximum=1000, - default=1, - precision=1, - controls_step=1, - slider_step=0.1, - scale="log", - ), - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def box_blur_node( - img: np.ndarray, - radius_x: float, - radius_y: float, -) -> np.ndarray: - if radius_x == 0 and radius_y == 0: - return img - - # you can't tell the difference between a float and an integer when the radius is large enough - radius_x = round(radius_x) if radius_x > 200 else radius_x - radius_y = round(radius_y) if radius_y > 200 else radius_y - - # both radii are integers - use_optimized_int = int(radius_x) == radius_x and int(radius_y) == radius_y - - if use_optimized_int: - # we can use an optimized box blur implementation - radius_x = int(round(radius_x)) - radius_y = int(round(radius_y)) - return cv2.blur( - img, (radius_x * 2 + 1, radius_y * 2 + 1), borderType=cv2.BORDER_REFLECT_101 - ) - - # cv2.blur is so much faster than the other methods, that it's worth manually separating the kernel. - # the idea here is that we blur with cv2.blur in x or y if we can - threshold = 15 - if radius_x >= threshold and int(radius_x) == radius_x: - img = cv2.blur( - img, (int(radius_x) * 2 + 1, 1), borderType=cv2.BORDER_REFLECT_101 - ) - radius_x = 1 - if radius_y >= threshold and int(radius_y) == radius_y: - img = cv2.blur( - img, (1, int(radius_y) * 2 + 1), borderType=cv2.BORDER_REFLECT_101 - ) - radius_y = 1 - - # Separable filter is faster for relatively small kernels, but after a certain size it becomes - # slower than filter2D's DFT implementation. The exact cutoff depends on the hardware. - avg_radius = math.sqrt(radius_x * radius_y) - use_sep = avg_radius < 70 - - if use_sep: - return cv2.sepFilter2D( - img, - -1, - get_kernel_1d(radius_x), - get_kernel_1d(radius_y), - borderType=cv2.BORDER_REFLECT_101, - ) - else: - return cv2.filter2D( - img, - -1, - get_kernel_2d(radius_x, radius_y), - borderType=cv2.BORDER_REFLECT_101, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/gaussian_blur.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/gaussian_blur.py deleted file mode 100644 index c3b300b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/gaussian_blur.py +++ /dev/null @@ -1,53 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.groups import linked_inputs_group -from nodes.impl.image_utils import fast_gaussian_blur -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import blur_group - - -@blur_group.register( - schema_id="chainner:image:gaussian_blur", - name="Gaussian Blur", - description="Apply Gaussian blur to an image.", - icon="MdBlurOn", - inputs=[ - ImageInput(), - linked_inputs_group( - SliderInput( - "Radius X", - minimum=0, - maximum=1000, - default=1, - precision=1, - controls_step=1, - slider_step=0.1, - scale="log", - ), - SliderInput( - "Radius Y", - minimum=0, - maximum=1000, - default=1, - precision=1, - controls_step=1, - slider_step=0.1, - scale="log", - ), - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def gaussian_blur_node( - img: np.ndarray, - sigma_x: float, - sigma_y: float, -) -> np.ndarray: - if sigma_x == 0 and sigma_y == 0: - return img - - return fast_gaussian_blur(img, sigma_x, sigma_y) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/lens_blur.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/lens_blur.py deleted file mode 100644 index 5a6af16..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/lens_blur.py +++ /dev/null @@ -1,184 +0,0 @@ -from __future__ import annotations - -import math -from functools import reduce -from typing import Dict, Literal - -import cv2 -import numpy as np - -from nodes.impl.image_utils import as_3d -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import blur_group - -# Lens blur adapted from GIMP Lens Blur -# Copyright (c) 2019 Davide Sandona' -# https://github.com/Davide-sd/GIMP-lens-blur.git - -kernel_scales = [1.4, 1.2, 1.2, 1.2, 1.2, 1.2] - -kernel_params = [ - [ - [0.862325, 1.624835, 0.767583, 1.862321], - ], - [ - [0.886528, 5.268909, 0.411259, -0.548794], - [1.960518, 1.558213, 0.513282, 4.56111], - ], - [ - [2.17649, 5.043495, 1.621035, -2.105439], - [1.019306, 9.027613, -0.28086, -0.162882], - [2.81511, 1.597273, -0.366471, 10.300301], - ], - [ - [4.338459, 1.553635, -5.767909, 46.164397], - [3.839993, 4.693183, 9.795391, -15.227561], - [2.791880, 8.178137, -3.048324, 0.302959], - [1.342190, 12.328289, 0.010001, 0.244650], - ], - [ - [4.892608, 1.685979, -22.356787, 85.91246], - [4.71187, 4.998496, 35.918936, -28.875618], - [4.052795, 8.244168, -13.212253, -1.578428], - [2.929212, 11.900859, 0.507991, 1.816328], - [1.512961, 16.116382, 0.138051, -0.01], - ], - [ - [5.143778, 2.079813, -82.326596, 111.231024], - [5.612426, 6.153387, 113.878661, 58.004879], - [5.982921, 9.802895, 39.479083, -162.028887], - [6.505167, 11.059237, -71.286026, 95.027069], - [3.869579, 14.81052, 1.405746, -3.704914], - [2.201904, 19.032909, -0.152784, -0.107988], - ], -] - -ParamKey = Literal["a", "b", "A", "B"] -Params = Dict[ParamKey, float] - - -def get_parameters(component_count: int) -> tuple[list[Params], float]: - parameter_index = max(0, min(component_count - 1, len(kernel_params) - 1)) - param_keys: list[ParamKey] = ["a", "b", "A", "B"] - parameter_dictionaries = [ - dict(zip(param_keys, b)) for b in kernel_params[parameter_index] - ] - return (parameter_dictionaries, kernel_scales[parameter_index]) - - -def complex_kernel_1d(radius: int, scale: float, a: float, b: float): - kernel_radius = radius - kernel_size = kernel_radius * 2 + 1 - ax = np.arange(-kernel_radius, kernel_radius + 1.0, dtype=np.float32) - ax = ax * scale * (1 / kernel_radius) - kernel_complex = np.zeros((kernel_size), dtype=np.complex64) - kernel_complex.real = np.exp(-a * (ax**2)) * np.cos(b * (ax**2)) # type: ignore - kernel_complex.imag = np.exp(-a * (ax**2)) * np.sin(b * (ax**2)) # type: ignore - return kernel_complex.reshape((1, kernel_size)) - - -def normalize_kernels(kernels: list[np.ndarray], params: list[Params]): - total = 0 - for k, p in zip(kernels, params): - for i in range(k.shape[1]): - for j in range(k.shape[1]): - total += p["A"] * ( - k[0, i].real * k[0, j].real - k[0, i].imag * k[0, j].imag - ) + p["B"] * (k[0, i].real * k[0, j].imag + k[0, i].imag * k[0, j].real) - scalar = 1 / math.sqrt(total) - return [k * scalar for k in kernels] - - -def weighted_sum(kernel: np.ndarray, params: Params) -> np.ndarray: - return np.add(kernel.real * params["A"], kernel.imag * params["B"]) - - -def lens_blur( - img: np.ndarray, radius: int, component_count: int, exposure_gamma: float -) -> np.ndarray: - img = np.ascontiguousarray(np.transpose(as_3d(img), (2, 0, 1)), dtype=np.float32) - parameters, scale = get_parameters(component_count) - components = [ - complex_kernel_1d(radius, scale, component_params["a"], component_params["b"]) - for component_params in parameters - ] - components = normalize_kernels(components, parameters) - img = np.power(img, exposure_gamma) - component_output = [] - for component, component_params in zip(components, parameters): - channels = [] - component_real = np.real(component) - component_imag = np.imag(component) - component_real_t = component_real.transpose() - component_imag_t = component_imag.transpose() - for channel in range(img.shape[0]): - inter_real = cv2.filter2D(img[channel], -1, component_real) - inter_imag = cv2.filter2D(img[channel], -1, component_imag) - final_1 = cv2.filter2D(inter_real, -1, component_real_t) - final_2 = cv2.filter2D(inter_real, -1, component_imag_t) - final_3 = cv2.filter2D(inter_imag, -1, component_real_t) - final_4 = cv2.filter2D(inter_imag, -1, component_imag_t) - final = final_1 - final_4 + 1j * (final_2 + final_3) # type: ignore - channels.append(final) - component_image = np.stack( - [weighted_sum(channel, component_params) for channel in channels] - ) - component_output.append(component_image) - output_image = reduce(np.add, component_output) - output_image = np.clip(output_image, 0, None) - output_image = np.power(output_image, 1.0 / exposure_gamma) - output_image = np.clip(output_image, 0, 1) - output_image = output_image.transpose(1, 2, 0) - return output_image - - -@blur_group.register( - schema_id="chainner:image:lens_blur", - name="Lens Blur", - description="Apply Lens blur to an image.", - icon="MdBlurOn", - inputs=[ - ImageInput(), - SliderInput( - "Radius", - minimum=0, - maximum=1000, - default=3, - precision=0, - controls_step=1, - scale="log", - ), - SliderInput( - "Components", - minimum=1, - maximum=6, - default=5, - precision=0, - controls_step=1, - ).with_docs( - "This controls the quality of the lens blur. More components will result in a more realistic blur, but will also be slower to compute." - ), - SliderInput( - "Exposure Gamma", - minimum=0.01, - maximum=100, - default=5, - precision=4, - controls_step=0.1, - scale="log", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def lens_blur_node( - img: np.ndarray, - radius: int, - component_count: int, - exposure_gamma: float, -) -> np.ndarray: - if radius == 0: - return img - - return lens_blur(img, radius, component_count, exposure_gamma) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/median_blur.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/median_blur.py deleted file mode 100644 index aad756d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/median_blur.py +++ /dev/null @@ -1,36 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import blur_group - - -@blur_group.register( - schema_id="chainner:image:median_blur", - name="Median Blur", - description="Apply median blur to an image.", - icon="MdBlurOn", - inputs=[ - ImageInput(), - SliderInput("Radius", minimum=0, maximum=1000, default=1, scale="log"), - ], - outputs=[ImageOutput(shape_as=0)], - limited_to_8bpc=True, -) -def median_blur_node( - img: np.ndarray, - radius: int, -) -> np.ndarray: - if radius == 0: - return img - else: - size = 2 * radius + 1 - if size <= 5: - return cv2.medianBlur(img, size) - else: # cv2 requires uint8 for kernel size > 5 - return cv2.medianBlur(to_uint8(img, normalized=True), size) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py deleted file mode 100644 index a1141e0..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py +++ /dev/null @@ -1,85 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import blur_group - - -@blur_group.register( - schema_id="chainner:image:bilateral_blur", - name="Surface Blur", - description="Apply surface/bilateral blur to an image.", - icon="MdBlurOn", - inputs=[ - ImageInput(), - SliderInput( - "Radius", - minimum=0, - maximum=100, - default=4, - controls_step=1, - scale="sqrt", - ), - SliderInput( - "Color Sigma", - controls_step=1, - default=25, - scale="log", - minimum=0, - maximum=1000, - ), - SliderInput( - "Space Sigma", - controls_step=1, - default=25, - scale="log", - minimum=0, - maximum=1000, - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def surface_blur_node( - img: np.ndarray, - radius: int, - sigma_color: int, - sigma_space: int, -) -> np.ndarray: - if radius == 0 or sigma_color == 0 or sigma_space == 0: - return img - - sigma_color_adjusted = sigma_color / 255 - diameter = radius * 2 + 1 - - _, _, c = get_h_w_c(img) - if c == 4: - rgb = img[:, :, :3] - alpha = img[:, :, 3] - rgb = cv2.bilateralFilter( - rgb, - diameter, - sigma_color_adjusted, - sigma_space, - borderType=cv2.BORDER_REFLECT_101, - ) - alpha = cv2.bilateralFilter( - alpha, - diameter, - sigma_color_adjusted, - sigma_space, - borderType=cv2.BORDER_REFLECT_101, - ) - return np.dstack((rgb, alpha)) - - return cv2.bilateralFilter( - img, - diameter, - sigma_color_adjusted, - sigma_space, - borderType=cv2.BORDER_REFLECT_101, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/correction/average_color_fix.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/correction/average_color_fix.py deleted file mode 100644 index de628a1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/correction/average_color_fix.py +++ /dev/null @@ -1,126 +0,0 @@ -from __future__ import annotations - -from math import ceil - -import cv2 -import numpy as np - -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import correction_group - - -@correction_group.register( - schema_id="chainner:image:average_color_fix", - name="Average Color Fix", - description="""Correct for upscaling model color shift by matching - the average color of the Input Image to that of a smaller Reference Image. - Using significant downscaling increases generalization of averaging effect - and can reduce artifacts in the output.""", - icon="MdAutoFixHigh", - inputs=[ - ImageInput("Image", channels=[3, 4]), - ImageInput("Reference Image", channels=[3, 4]), - NumberInput( - "Reference Image Scale Factor", - precision=4, - controls_step=12.5, - maximum=100.0, - default=12.5, - unit="%", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def average_color_fix_node( - input_img: np.ndarray, ref_img: np.ndarray, scale_factor: float -) -> np.ndarray: - if scale_factor != 100.0: - # Make sure reference image dims are not resized to 0 - h, w, _ = get_h_w_c(ref_img) - out_dims = ( - max(ceil(w * (scale_factor / 100)), 1), - max(ceil(h * (scale_factor / 100)), 1), - ) - - ref_img = resize(ref_img, out_dims, filter=ResizeFilter.BOX) - - input_h, input_w, input_c = get_h_w_c(input_img) - ref_h, ref_w, ref_c = get_h_w_c(ref_img) - - assert ( - ref_w < input_w and ref_h < input_h - ), "Image must be larger than Reference Image" - - # Find the diff of both images - - # Downscale the input image - downscaled_input = resize(input_img, (ref_w, ref_h), filter=ResizeFilter.BOX) - - # adjust channels - alpha = None - downscaled_alpha = None - ref_alpha = None - if input_c > 3: - alpha = input_img[:, :, 3:4] - input_img = input_img[:, :, :3] - downscaled_alpha = downscaled_input[:, :, 3:4] - downscaled_input = downscaled_input[:, :, :3] - if ref_c > 3: - ref_alpha = ref_img[:, :, 3:4] - ref_img = ref_img[:, :, :3] - - # Get difference between the reference image and downscaled input - downscaled_diff = ref_img - downscaled_input # type: ignore - - downscaled_alpha_diff = None - if ref_alpha is not None or downscaled_alpha is not None: - # Don't alter RGB pixels if either the input or reference pixel is - # fully transparent, since RGB diff is indeterminate for those pixels. - if ref_alpha is not None and downscaled_alpha is not None: - invalid_alpha_mask = (ref_alpha == 0) | (downscaled_alpha == 0) - elif ref_alpha is not None: - invalid_alpha_mask = ref_alpha == 0 - else: - invalid_alpha_mask = downscaled_alpha == 0 - invalid_alpha_indices = np.nonzero(invalid_alpha_mask) - downscaled_diff[invalid_alpha_indices] = 0 - - if ref_alpha is not None and downscaled_alpha is not None: - downscaled_alpha_diff = ref_alpha - downscaled_alpha # type: ignore - - # Upsample the difference - diff = cv2.resize( - downscaled_diff, - (input_w, input_h), - interpolation=cv2.INTER_CUBIC, - ) - - alpha_diff = None - if downscaled_alpha_diff is not None: - alpha_diff = cv2.resize( - downscaled_alpha_diff, - (input_w, input_h), - interpolation=cv2.INTER_CUBIC, - ) - alpha_diff = np.expand_dims(alpha_diff, 2) - - if alpha_diff is not None: - # Don't alter alpha pixels if the input pixel is fully transparent, since - # doing so would expose indeterminate RGB data. - invalid_rgb_mask = alpha == 0 - invalid_rgb_indices = np.nonzero(invalid_rgb_mask) - alpha_diff[invalid_rgb_indices] = 0 - - result = input_img + diff - if alpha_diff is not None: - alpha = alpha + alpha_diff # type: ignore - - # add alpha back in - if alpha is not None: - result = np.concatenate([result, alpha], axis=2) - - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/correction/color_transfer.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/correction/color_transfer.py deleted file mode 100644 index 666b1cb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/correction/color_transfer.py +++ /dev/null @@ -1,120 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import if_enum_group -from nodes.impl.color_transfer.linear_histogram import linear_histogram_transfer -from nodes.impl.color_transfer.mean_std import ( - OverflowMethod, - TransferColorSpace, - mean_std_transfer, -) -from nodes.impl.color_transfer.principal_color import principal_color_transfer -from nodes.properties.inputs import BoolInput, EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import correction_group - - -class TransferColorAlgorithm(Enum): - MEAN_STD = "mean_std" - LINEAR_HISTOGRAM = "linear_histogram" - PRINCIPAL_COLOR = "principal_color" - - -TRANSFER_COLOR_ALGORITHM_LABELS = { - TransferColorAlgorithm.MEAN_STD: "Mean+Std", - TransferColorAlgorithm.LINEAR_HISTOGRAM: "Linear Histogram", - TransferColorAlgorithm.PRINCIPAL_COLOR: "Principal Color", -} - - -@correction_group.register( - schema_id="chainner:image:color_transfer", - name="Color Transfer", - description="""Transfers colors from reference image. - Different combinations of settings may perform better for - different images. Try multiple setting combinations to find - best results.""", - icon="MdInput", - inputs=[ - ImageInput("Image", channels=[3, 4]), - ImageInput("Reference Image", channels=[3, 4]), - EnumInput( - TransferColorAlgorithm, - label="Algorithm", - option_labels=TRANSFER_COLOR_ALGORITHM_LABELS, - default=TransferColorAlgorithm.MEAN_STD, - ).with_id(5), - if_enum_group(5, TransferColorAlgorithm.MEAN_STD)( - EnumInput( - TransferColorSpace, - label="Colorspace", - option_labels={TransferColorSpace.LAB: "L*a*b*"}, - ).with_id(2), - EnumInput(OverflowMethod).with_id(3), - BoolInput("Reciprocal Scaling Factor", default=True).with_id(4), - ), - ], - outputs=[ImageOutput("Image", shape_as=0)], -) -def color_transfer_node( - img: np.ndarray, - ref_img: np.ndarray, - algorithm: TransferColorAlgorithm, - colorspace: TransferColorSpace, - overflow_method: OverflowMethod, - reciprocal_scale: bool, -) -> np.ndarray: - _, _, img_c = get_h_w_c(img) - - # Preserve alpha - alpha = None - if img_c == 4: - alpha = img[:, :, 3] - bgr_img = img[:, :, :3] - - _, _, ref_img_c = get_h_w_c(ref_img) - - ref_alpha = None - if ref_img_c == 4: - ref_alpha = ref_img[:, :, 3] - bgr_ref_img = ref_img[:, :, :3] - - # Don't process RGB data if the pixel is fully transparent, since - # such RGB data is indeterminate. - valid_rgb_indices = np.ones(img.shape[:-1], dtype=bool) - if alpha is not None: - valid_rgb_indices = alpha > 0 - - ref_valid_rgb_indices = np.ones(ref_img.shape[:-1], dtype=bool) - if ref_alpha is not None: - ref_valid_rgb_indices = ref_alpha > 0 - - transfer = bgr_img - if algorithm == TransferColorAlgorithm.MEAN_STD: - transfer = mean_std_transfer( - bgr_img, - bgr_ref_img, - colorspace, - overflow_method, - reciprocal_scale=reciprocal_scale, - valid_indices=valid_rgb_indices, - ref_valid_indices=ref_valid_rgb_indices, - ) - elif algorithm == TransferColorAlgorithm.LINEAR_HISTOGRAM: - transfer = linear_histogram_transfer( - bgr_img, bgr_ref_img, valid_rgb_indices, ref_valid_rgb_indices - ) - elif algorithm == TransferColorAlgorithm.PRINCIPAL_COLOR: - transfer = principal_color_transfer( - bgr_img, bgr_ref_img, valid_rgb_indices, ref_valid_rgb_indices - ) - - if alpha is not None: - transfer = np.dstack((transfer, alpha)) - - return transfer diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/canny_edge_detection.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/canny_edge_detection.py deleted file mode 100644 index acb1553..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/canny_edge_detection.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -import navi -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:canny_edge_detection", - name="Canny Edge Detection", - description=( - "Detect the edges of the input image and output as black and white image." - ), - icon="MdAutoFixHigh", - inputs=[ - ImageInput(), - NumberInput("Lower Threshold", minimum=0, default=100), - NumberInput("Upper Threshold", minimum=0, default=300), - ], - outputs=[ImageOutput(image_type=navi.Image(size_as="Input0"), channels=1)], -) -def canny_edge_detection_node( - img: np.ndarray, - t_lower: int, - t_upper: int, -) -> np.ndarray: - return cv2.Canny(to_uint8(img, normalized=True), t_lower, t_upper) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/convolve.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/convolve.py deleted file mode 100644 index c59ed81..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/convolve.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.properties.inputs import ImageInput, NumberInput, TextInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:image_convolve", - name="Convolve", - description="Convolves input image with input kernel", - icon="MdAutoFixHigh", - inputs=[ - ImageInput("Image"), - TextInput( - "Kernel String", - placeholder="0 0 0\n0 1 0\n0 0 0", - multiline=True, - has_handle=False, - min_length=1, - ), - NumberInput("Padding", minimum=0, default=0), - ], - outputs=[ - ImageOutput( - image_type=""" - let w = Input0.width; - let h = Input0.height; - - let kernel = Input1; - - let padding = Input2; - - Image { - width: w + padding * 2, - height: h + padding * 2, - } - """, - ) - ], -) -def convolve_node( - img: np.ndarray, - kernel_in: str, - padding: int, -) -> np.ndarray: - kernel = np.stack([l.split() for l in kernel_in.splitlines()], axis=0).astype(float) - - kernel = np.flipud(np.fliplr(kernel)) - - img = cv2.copyMakeBorder( - img, - top=padding, - left=padding, - right=padding, - bottom=padding, - borderType=cv2.BORDER_CONSTANT, - value=(0.0,), - ) - - output = cv2.filter2D(img, -1, kernel) - - return output diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/dilate.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/dilate.py deleted file mode 100644 index 8d3fdeb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/dilate.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -class MorphShape(Enum): - RECTANGLE = cv2.MORPH_RECT - ELLIPSE = cv2.MORPH_ELLIPSE - CROSS = cv2.MORPH_CROSS - - -@miscellaneous_group.register( - schema_id="chainner:image:dilate", - name="Dilate", - description="Dilate an image", - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput(), - EnumInput( - MorphShape, - option_labels={ - MorphShape.RECTANGLE: "Square", - MorphShape.ELLIPSE: "Circle", - MorphShape.CROSS: "Cross", - }, - ), - SliderInput( - "Radius", - minimum=0, - maximum=1000, - default=1, - controls_step=1, - scale="log", - ), - SliderInput( - "Iterations", - minimum=0, - maximum=1000, - default=1, - controls_step=1, - scale="log", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def dilate_node( - img: np.ndarray, - morph_shape: MorphShape, - radius: int, - iterations: int, -) -> np.ndarray: - if radius == 0 or iterations == 0: - return img - - size = 2 * radius + 1 - element = cv2.getStructuringElement(morph_shape.value, (size, size)) - - return cv2.dilate(img, element, iterations=iterations) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/distance_transform.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/distance_transform.py deleted file mode 100644 index 2d2fd4e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/distance_transform.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np -from chainner_ext import esdf - -from nodes.impl.image_utils import as_3d, to_uint8 -from nodes.properties.inputs import BoolInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -def binary_sdf(img: np.ndarray, spread: float) -> np.ndarray: - img = as_3d(to_uint8(img, normalized=True)) - img[img < 128] = 0 - img[img >= 128] = 255 - - black_dist = np.empty(shape=img.shape, dtype=np.float32) - white_dist = np.empty(shape=img.shape, dtype=np.float32) - - cv2.distanceTransform( - src=img, - distanceType=cv2.DIST_L2, - maskSize=5, - dst=black_dist, - dstType=cv2.CV_32F, # type: ignore - ) - cv2.distanceTransform( - src=255 - img, - distanceType=cv2.DIST_L2, - maskSize=5, - dst=white_dist, - dstType=cv2.CV_32F, # type: ignore - ) - - img1 = img.ravel() - signed_distance = np.empty(shape=img.shape, dtype=np.float32).ravel() - - signed_distance[img1 == 255] = black_dist.ravel()[img1 == 255] / spread / 2 + 0.5 - signed_distance[img1 == 0] = 0.5 - white_dist.ravel()[img1 == 0] / spread / 2 - - signed_distance = np.clip(signed_distance, 0, 1) - - return signed_distance.reshape(img.shape) - - -@miscellaneous_group.register( - schema_id="chainner:image:distance_transform", - name="Distance Transform", - description="Perform a distance transform on a monochrome bitmap image, producing a signed distance field.", - icon="MdBlurOff", - inputs=[ - ImageInput(channels=1), - NumberInput("Spread", minimum=1, default=4), - BoolInput("Sub-pixel precision", default=False).with_docs( - "If enabled, then anti-aliasing will be accounted for. If not enabled, then the image will be converted to binary (either black or white) before processing.", - "Enabling this option will significantly improve the results of anti-aliased shapes, but it cannot be used on anything else. It assumes strictly binary shapes (with optional anti-aliasing), and will return incorrect results for e.g. blurry images. If you cannot guarantee binary image, use the `chainner:image:threshold` node with *Anti-aliasing* enabled.", - "Sub-pixel distance transform is implemented using the excellent [ESDF algorithm](https://acko.net/blog/subpixel-distance-transform/) by Steven Wittens.", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def distance_transform_node( - img: np.ndarray, - spread: int, - use_esdf: bool, -) -> np.ndarray: - if use_esdf: - return esdf(img, spread * 2, 0.5, False, True) - return binary_sdf(img, spread) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/edge_detection.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/edge_detection.py deleted file mode 100644 index d20333f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/edge_detection.py +++ /dev/null @@ -1,246 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.groups import if_enum_group -from nodes.impl.image_utils import fast_gaussian_blur -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import miscellaneous_group - - -class Algorithm(Enum): - SOBEL = 1 - SCHARR = 2 - GRADIENT = 3 - DIFFERENTIAL = 4 - ROBERTS = 5 - PREWITT_COMPASS = 6 - LAPLACIAN = 7 - LAPLACIAN_DENOISE = 8 - DIFFERENCE_OF_GAUSSIAN = 9 - - -class GradientComponent(Enum): - MAGNITUDE = 1 - X = 2 - Y = 3 - - -FILTER_X: dict[Algorithm, np.ndarray] = { - Algorithm.SOBEL: ( - 0.25 - * np.array( - [ - [-1, 0, +1], - [-2, 0, +2], - [-1, 0, +1], - ] - ).astype(np.float32) - ), - Algorithm.SCHARR: ( - (1 / 256) - * np.array( - [ - [-47, 0, +47], - [-162, 0, +162], - [-47, 0, +47], - ] - ).astype(np.float32) - ), - Algorithm.GRADIENT: ( - np.array( - [ - [0, 0, 0], - [-1, 1, 0], - [0, 0, 0], - ] - ).astype(np.float32) - ), - Algorithm.DIFFERENTIAL: ( - 0.5 - * np.array( - [ - [0, 0, 0], - [0, -1, +1], - [0, -1, +1], - ] - ).astype(np.float32) - ), - Algorithm.ROBERTS: ( - np.array( - [ - [0, 0, 0], - [0, +1, 0], - [0, 0, -1], - ] - ).astype(np.float32) - ), -} - -FILTER_Y: dict[Algorithm, np.ndarray] = { - Algorithm.DIFFERENTIAL: ( - 0.5 - * np.array( - [ - [0, 0, 0], - [0, +1, +1], - [0, -1, -1], - ] - ).astype(np.float32) - ), - Algorithm.ROBERTS: ( - np.array( - [ - [0, 0, 0], - [0, 0, +1], - [0, -1, 0], - ] - ).astype(np.float32) - ), -} - -LAPLACE_KERNEL = 0.25 * np.array( - [ - [1, 1, 1], - [1, -8, 1], - [1, 1, 1], - ] -).astype(np.float32) - - -@miscellaneous_group.register( - schema_id="chainner:image:edge_detection", - name="Edge Detection", - description=( - "Detect the edges of the input image with a variety of algorithms. The output will be the magnitude of the gradient per channel. The alpha channel will be copied from the input image." - ), - icon="MdAutoFixHigh", - inputs=[ - ImageInput(), - SliderInput( - "Amount", minimum=0, default=1, maximum=10, precision=2, scale="log" - ), - EnumInput(Algorithm).with_id(2), - if_enum_group( - 2, - ( - Algorithm.SOBEL, - Algorithm.SCHARR, - Algorithm.GRADIENT, - Algorithm.DIFFERENTIAL, - ), - )( - EnumInput(GradientComponent), - ), - if_enum_group(2, Algorithm.DIFFERENCE_OF_GAUSSIAN)( - SliderInput("Radius 1", minimum=0, default=1, maximum=10, precision=3), - SliderInput("Radius 2", minimum=0, default=2, maximum=20, precision=3), - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def edge_detection_node( - img: np.ndarray, - amount: float, - algorithm: Algorithm, - gradient_component: GradientComponent, - radius_1: float, - radius_2: float, -) -> np.ndarray: - c = get_h_w_c(img)[2] - alpha = None - if c >= 4: - alpha = img[:, :, 3:] - img = img[:, :, :3] - - if algorithm in ( - Algorithm.SOBEL, - Algorithm.SCHARR, - Algorithm.GRADIENT, - Algorithm.DIFFERENTIAL, - Algorithm.ROBERTS, - ): - filter_x = FILTER_X[algorithm] - filter_y = FILTER_Y.get(algorithm) - - def g_x() -> np.ndarray: - return cv2.filter2D(img, -1, filter_x) - - def g_y() -> np.ndarray: - filter = filter_y if filter_y is not None else np.rot90(filter_x, 1) - return cv2.filter2D(img, -1, filter) - - if algorithm == Algorithm.ROBERTS: - gradient_component = GradientComponent.MAGNITUDE - - if gradient_component == GradientComponent.MAGNITUDE: - img = np.hypot(g_x(), g_y()) * (amount / 2) - elif gradient_component == GradientComponent.X: - img = g_x() * (amount / 2) + 0.5 - elif gradient_component == GradientComponent.Y: - img = g_y() * (amount / 2) + 0.5 - else: - raise ValueError(f"Invalid gradient component: {gradient_component}") - - elif algorithm == Algorithm.PREWITT_COMPASS: - img = prewitt_compass(img) * (amount / 2) - - elif algorithm == Algorithm.LAPLACIAN: - img = cv2.filter2D(img, -1, LAPLACE_KERNEL) * amount # type: ignore - - elif algorithm == Algorithm.LAPLACIAN_DENOISE: - img = laplacian_denoise(img) * amount - - elif algorithm == Algorithm.DIFFERENCE_OF_GAUSSIAN: - g1 = fast_gaussian_blur(img, radius_1) - g2 = fast_gaussian_blur(img, radius_2) - img = (g1 - g2) * amount - - img = np.clip(img, 0, 1) # type: ignore - - if alpha is not None: - img = np.dstack((img, alpha)) - return img - - -def prewitt_compass(img: np.ndarray) -> np.ndarray: - filter_0 = 0.25 * np.array( - [ - [-1, +1, +1], - [-1, -2, +1], - [-1, +1, +1], - ] - ).astype(np.float32) - filter_45 = 0.25 * np.array( - [ - [+1, +1, +1], - [-1, -2, +1], - [-1, -1, +1], - ] - ).astype(np.float32) - - g = np.abs(cv2.filter2D(img, -1, filter_0)) - for i in range(1, 4): - g = np.maximum(g, np.abs(cv2.filter2D(img, -1, np.rot90(filter_0, i)))) - for i in range(4): - g = np.maximum(g, np.abs(cv2.filter2D(img, -1, np.rot90(filter_45, i)))) - - return g - - -def laplacian_denoise(img: np.ndarray) -> np.ndarray: - # Modified version of Gimp's Laplace filter. - # https://gitlab.gnome.org/GNOME/gegl/-/blob/master/opencl/edge-laplace.cl - # Changes: Instead of using the laplacian to get the sign of the magnitude, - # we use it as a clipped factor. This eliminates abrupt changes in the - # output image and has an even stronger denoising effect. - max_val = cv2.dilate(img, cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))) - min_val = cv2.erode(img, cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))) - laplace = cv2.filter2D(img, -1, LAPLACE_KERNEL) - return np.maximum(max_val - img, img - min_val) * np.clip(laplace * 10, -0.75, 0.75) # type: ignore diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/erode.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/erode.py deleted file mode 100644 index e4a4f5c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/erode.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -class MorphShape(Enum): - RECTANGLE = cv2.MORPH_RECT - ELLIPSE = cv2.MORPH_ELLIPSE - CROSS = cv2.MORPH_CROSS - - -@miscellaneous_group.register( - schema_id="chainner:image:erode", - name="Erode", - description="Erode an image", - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput(), - EnumInput( - MorphShape, - option_labels={ - MorphShape.RECTANGLE: "Square", - MorphShape.ELLIPSE: "Circle", - MorphShape.CROSS: "Cross", - }, - ), - SliderInput( - "Radius", - minimum=0, - maximum=1000, - default=1, - controls_step=1, - scale="log", - ), - SliderInput( - "Iterations", - minimum=0, - maximum=1000, - default=1, - controls_step=1, - scale="log", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def erode_node( - img: np.ndarray, - morph_shape: MorphShape, - radius: int, - iterations: int, -) -> np.ndarray: - if radius == 0 or iterations == 0: - return img - - size = 2 * radius + 1 - element = cv2.getStructuringElement(morph_shape.value, (size, size)) - - return cv2.erode(img, element, iterations=iterations) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/high_pass.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/high_pass.py deleted file mode 100644 index 4abfadd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/high_pass.py +++ /dev/null @@ -1,111 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import if_enum_group, required -from nodes.impl.image_utils import fast_gaussian_blur -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import miscellaneous_group - - -class BlurMode(Enum): - GAUSSIAN = 0 - CUSTOM = 1 - - -@miscellaneous_group.register( - schema_id="chainner:image:high_pass", - name="High Pass", - description=[ - "Apply High Pass filter on image.", - "A high pass filter is a filter will remove all low-frequency detail below a certain threshold. This will result in an image that is mostly edges and high-frequency detail.", - "In more concrete terms, this means that the node return `contrast * (image - blurred) + 0.5`. `blurred` is either the input image with a gaussian blur applied to it or a custom blurred image. Gaussian blur is recommended, because it is correct from a single-processing point of view.", - "Note: This node will leave the alpha channel of the image (if it has one) unchanged.", - ], - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput(channels=[1, 3, 4]), - EnumInput(BlurMode, default=BlurMode.GAUSSIAN, preferred_style="tabs").with_id( - 3 - ), - if_enum_group(3, BlurMode.GAUSSIAN)( - SliderInput( - "Blur Radius", - minimum=0, - maximum=1000, - default=3, - precision=1, - controls_step=1, - slider_step=0.1, - scale="log", - ).with_id(1), - ), - if_enum_group(3, BlurMode.CUSTOM)( - required()( - ImageInput("Blurred Image", channels=[1, 3, 4]) - .make_optional() - .with_id(4), - ), - ), - SliderInput( - "Contrast", - minimum=0, - maximum=100, - default=1, - precision=2, - controls_step=0.1, - scale="log-offset", - ).with_id(2), - ], - outputs=[ - ImageOutput( - image_type=""" - let i = Input0; - let mode = Input3; - let blurred = Input4; - - match mode { - BlurMode::Gaussian => i, - BlurMode::Custom => { - let valid = i == blurred; - - if valid { i } else { never } - }, - } - """ - ), - ], -) -def high_pass_node( - img: np.ndarray, - mode: BlurMode, - radius: float, - blurred: np.ndarray | None, - contrast: float, -) -> np.ndarray: - _, _, c = get_h_w_c(img) - alpha = None - if c > 3: - alpha = img[:, :, 3] - img = img[:, :, :3] - - if mode == BlurMode.GAUSSIAN: - blurred = fast_gaussian_blur(img, radius) - elif mode == BlurMode.CUSTOM: - assert blurred is not None, "Expected a blurred image to be given." - - assert ( - blurred.shape == img.shape - ), "Expected blurred image to have same shape as the input image." - - img = contrast * (img - blurred) + 0.5 - - if alpha is not None: - img = np.dstack((img, alpha)) - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/pixelate.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/pixelate.py deleted file mode 100644 index d14b9be..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/miscellaneous/pixelate.py +++ /dev/null @@ -1,61 +0,0 @@ -from __future__ import annotations - -import math - -import cv2 -import numpy as np - -from nodes.groups import linked_inputs_group -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:pixelate", - name="Pixelate", - description="Pixelate an image.", - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput(), - linked_inputs_group( - SliderInput("Size X", minimum=1, maximum=1024, default=10, scale="log"), - SliderInput("Size Y", minimum=1, maximum=1024, default=10, scale="log"), - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def pixelate_node( - img: np.ndarray, - size_x: int, - size_y: int, -) -> np.ndarray: - block_sizes = (size_x, size_y) - height, width = img.shape[:2] - - pad_x = (block_sizes[0] - width % block_sizes[0]) % block_sizes[0] - pad_y = (block_sizes[1] - height % block_sizes[1]) % block_sizes[1] - img = cv2.copyMakeBorder(img, 0, pad_y, 0, pad_x, cv2.BORDER_REFLECT_101) - - num_blocks_x = math.ceil(width / block_sizes[0]) - num_blocks_y = math.ceil(height / block_sizes[1]) - - blocks = img[: num_blocks_y * block_sizes[1], : num_blocks_x * block_sizes[0]] - blocks = ( - blocks.reshape( - (num_blocks_y, block_sizes[1], num_blocks_x, block_sizes[0], img.shape[-1]) - ) - if len(img.shape) > 2 - else blocks.reshape( - (num_blocks_y, block_sizes[1], num_blocks_x, block_sizes[0]) - ) - ) - - average_colors = np.mean(np.mean(blocks, axis=1), axis=2) # type: ignore - - repeated_colors = np.repeat( - np.repeat(average_colors, block_sizes[0], axis=1), block_sizes[1], axis=0 - ) - - return repeated_colors[:height, :width] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/add_noise.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/add_noise.py deleted file mode 100644 index bb8046c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/add_noise.py +++ /dev/null @@ -1,84 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import seed_group -from nodes.impl.noise import ( - NoiseColor, - gaussian_noise, - poisson_noise, - salt_and_pepper_noise, - speckle_noise, - uniform_noise, -) -from nodes.properties.inputs import EnumInput, ImageInput, SeedInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.seed import Seed - -from .. import noise_group - - -class NoiseType(Enum): - GAUSSIAN = "gaussian" - UNIFORM = "uniform" - SALT_AND_PEPPER = "salt_and_pepper" - SPECKLE = "speckle" - POISSON = "poisson" - - -@noise_group.register( - schema_id="chainner:image:add_noise", - name="Add Noise", - description="Add various kinds of noise to an image.", - icon="CgEditNoise", - inputs=[ - ImageInput(channels=[1, 3, 4]), - EnumInput( - NoiseType, option_labels={NoiseType.SALT_AND_PEPPER: "Salt & Pepper"} - ), - EnumInput( - NoiseColor, - option_labels={ - NoiseColor.RGB: "Color", - NoiseColor.GRAY: "Monochrome", - }, - ), - SliderInput("Amount", minimum=0, maximum=100, default=50), - seed_group(SeedInput()), - ], - outputs=[ - ImageOutput( - image_type=""" - Image { - width: Input0.width, - height: Input0.height, - channels: max( - Input0.channels, - match Input2 { NoiseColor::Rgb => 3, NoiseColor::Gray => 1 } - ) - }""", - assume_normalized=True, - ) - ], -) -def add_noise_node( - img: np.ndarray, - noise_type: NoiseType, - noise_color: NoiseColor, - amount: int, - seed: Seed, -) -> np.ndarray: - if noise_type == NoiseType.GAUSSIAN: - return gaussian_noise(img, amount / 100, noise_color, seed.value) - elif noise_type == NoiseType.UNIFORM: - return uniform_noise(img, amount / 100, noise_color, seed.value) - elif noise_type == NoiseType.SALT_AND_PEPPER: - return salt_and_pepper_noise(img, amount / 100, noise_color, seed.value) - elif noise_type == NoiseType.POISSON: - return poisson_noise(img, amount / 100, noise_color, seed.value) - elif noise_type == NoiseType.SPECKLE: - return speckle_noise(img, amount / 100, noise_color, seed.value) - else: - raise ValueError(f"Unknown noise type: {noise_type}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/denoise.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/denoise.py deleted file mode 100644 index d71214f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/denoise.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.groups import Condition, if_group -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import noise_group - - -@noise_group.register( - schema_id="chainner:image:fast_nlmeans", - name="Denoise", - description="Use the fast Non-Local Means algorithm to denoise an image.", - icon="CgEditNoise", - inputs=[ - ImageInput("Image", channels=[1, 3, 4]), - SliderInput( - "Luminance strength", - minimum=0, - maximum=50, - default=3.0, - precision=1, - controls_step=0.1, - slider_step=0.1, - ), - if_group(Condition.type(0, "Image { channels: 3 | 4 }"))( - SliderInput( - "Color strength", - minimum=0.0, - maximum=50.0, - default=3.0, - precision=1, - controls_step=0.1, - slider_step=0.1, - ) - ), - NumberInput("Patch radius", minimum=1, default=3, maximum=30, precision=0), - NumberInput("Search radius", minimum=1, default=10, maximum=30, precision=0), - ], - outputs=[ImageOutput(shape_as=0)], - limited_to_8bpc=True, -) -def denoise_node( - img: np.ndarray, - h: float, - h_color: float, - patch_radius: int, - search_radius: int, -) -> np.ndarray: - _, _, c = get_h_w_c(img) - image_array = to_uint8(img) - - patch_window_size = 2 * patch_radius + 1 - search_window_size = 2 * search_radius + 1 - - if c == 1: - denoised = cv2.fastNlMeansDenoising( - src=image_array, - h=h, - templateWindowSize=patch_window_size, - searchWindowSize=search_window_size, - ) - - else: - rgb = image_array[:, :, :3] - alpha = None - if c == 4: - alpha = image_array[:, :, 3] - - denoised = cv2.fastNlMeansDenoisingColored( - src=rgb, - h=h, - hColor=h_color, - templateWindowSize=patch_window_size, - searchWindowSize=search_window_size, - ) - - if alpha is not None: - denoised = np.dstack((denoised, alpha)) - - return denoised diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/fast_nl_means.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/fast_nl_means.py deleted file mode 100644 index 8650d84..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/noise/fast_nl_means.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.groups import Condition, if_group -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import noise_group - - -@noise_group.register( - schema_id="chainner:image:fast_nlmeans", - name="Fast NL means", - description="Use the fast Non-Local Means algorithm to denoise an image.", - icon="CgEditNoise", - inputs=[ - ImageInput("Image", channels=[1, 3, 4]), - SliderInput( - "Luminance strength", - minimum=0, - maximum=50, - default=3.0, - precision=1, - controls_step=0.1, - slider_step=0.1, - ), - if_group(Condition.type(0, "Image { channels: 3 | 4 }"))( - SliderInput( - "Color strength", - minimum=0.0, - maximum=50.0, - default=3.0, - precision=1, - controls_step=0.1, - slider_step=0.1, - ) - ), - NumberInput("Patch radius", minimum=1, default=3, maximum=30, precision=0), - NumberInput("Search radius", minimum=1, default=10, maximum=30, precision=0), - ], - outputs=[ImageOutput(image_type="Input0")], - limited_to_8bpc=True, -) -def fast_nl_means_node( - img: np.ndarray, - h: float, - h_color: float, - patch_radius: int, - search_radius: int, -) -> np.ndarray: - _, _, c = get_h_w_c(img) - image_array = to_uint8(img) - - patch_window_size = 2 * patch_radius + 1 - search_window_size = 2 * search_radius + 1 - - if c == 1: - denoised = cv2.fastNlMeansDenoising( - src=image_array, - h=h, - templateWindowSize=patch_window_size, - searchWindowSize=search_window_size, - ) - - else: - rgb = image_array[:, :, :3] - alpha = None - if c == 4: - alpha = image_array[:, :, 3] - - denoised = cv2.fastNlMeansDenoisingColored( - src=rgb, - h=h, - hColor=h_color, - templateWindowSize=patch_window_size, - searchWindowSize=search_window_size, - ) - - if alpha is not None: - denoised = np.dstack((denoised, alpha)) - - return denoised diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/dither.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/dither.py deleted file mode 100644 index c79f0e9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/dither.py +++ /dev/null @@ -1,111 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np -from chainner_ext import ( - UniformQuantization, - error_diffusion_dither, - ordered_dither, - quantize, - riemersma_dither, -) - -from nodes.groups import if_enum_group -from nodes.impl.dithering.constants import ( - DIFFUSION_ALGORITHM_MAP, - ERROR_PROPAGATION_MAP_LABELS, - THRESHOLD_MAP_LABELS, - ErrorDiffusionMap, - ThresholdMap, -) -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import quantize_group - -_THRESHOLD_MAP: dict[ThresholdMap, int] = { - ThresholdMap.BAYER_2: 2, - ThresholdMap.BAYER_4: 4, - ThresholdMap.BAYER_8: 8, - ThresholdMap.BAYER_16: 16, -} - - -class UniformDitherAlgorithm(Enum): - NONE = "None" - ORDERED = "Ordered" - DIFFUSION = "Diffusion" - RIEMERSMA = "Riemersma" - - -UNIFORM_DITHER_ALGORITHM_LABELS = { - UniformDitherAlgorithm.NONE: "No dithering", - UniformDitherAlgorithm.ORDERED: "Ordered Dithering", - UniformDitherAlgorithm.DIFFUSION: "Error Diffusion", - UniformDitherAlgorithm.RIEMERSMA: "Riemersma Dithering", -} - - -@quantize_group.register( - schema_id="chainner:image:dither", - name="Dither", - description="Apply one of a variety of dithering algorithms with a uniform (evenly-spaced) palette.", - icon="MdShowChart", - inputs=[ - ImageInput(channels=[1, 3, 4]), - NumberInput("Colors per channel", minimum=2, default=8), - EnumInput( - UniformDitherAlgorithm, - option_labels=UNIFORM_DITHER_ALGORITHM_LABELS, - default=UniformDitherAlgorithm.DIFFUSION, - ).with_id(2), - if_enum_group(2, UniformDitherAlgorithm.ORDERED)( - EnumInput( - ThresholdMap, - option_labels=THRESHOLD_MAP_LABELS, - default=ThresholdMap.BAYER_16, - ).with_id(3), - ), - if_enum_group(2, UniformDitherAlgorithm.DIFFUSION)( - EnumInput( - ErrorDiffusionMap, - option_labels=ERROR_PROPAGATION_MAP_LABELS, - default=ErrorDiffusionMap.FLOYD_STEINBERG, - ).with_id(4), - ), - if_enum_group(2, UniformDitherAlgorithm.RIEMERSMA)( - NumberInput( - "History Length", - minimum=2, - default=16, - ).with_id(5), - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def dither_node( - img: np.ndarray, - num_colors: int, - dither_algorithm: UniformDitherAlgorithm, - threshold_map: ThresholdMap, - error_diffusion_map: ErrorDiffusionMap, - history_length: int, -) -> np.ndarray: - quant = UniformQuantization(num_colors) - - if dither_algorithm == UniformDitherAlgorithm.NONE: - return quantize(img, quant) - elif dither_algorithm == UniformDitherAlgorithm.ORDERED: - map_size = _THRESHOLD_MAP[threshold_map] - return ordered_dither(img, quant, map_size) - elif dither_algorithm == UniformDitherAlgorithm.DIFFUSION: - algorithm = DIFFUSION_ALGORITHM_MAP[error_diffusion_map] - return error_diffusion_dither(img, quant, algorithm) - elif dither_algorithm == UniformDitherAlgorithm.RIEMERSMA: - return riemersma_dither( - img, - quant, - history_length, - 1 / history_length, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/dither_palette.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/dither_palette.py deleted file mode 100644 index 5277441..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/dither_palette.py +++ /dev/null @@ -1,96 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np -from chainner_ext import ( - PaletteQuantization, - error_diffusion_dither, - quantize, - riemersma_dither, -) - -import navi -from nodes.groups import if_enum_group -from nodes.impl.dithering.constants import ( - DIFFUSION_ALGORITHM_MAP, - ERROR_PROPAGATION_MAP_LABELS, - ErrorDiffusionMap, -) -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import quantize_group - - -class PaletteDitherAlgorithm(Enum): - NONE = "None" - DIFFUSION = "Diffusion" - RIEMERSMA = "Riemersma" - - -PALETTE_DITHER_ALGORITHM_LABELS = { - PaletteDitherAlgorithm.NONE: "No dithering", - PaletteDitherAlgorithm.DIFFUSION: "Error Diffusion", - PaletteDitherAlgorithm.RIEMERSMA: "Riemersma Dithering", -} - - -@quantize_group.register( - schema_id="chainner:image:palette_dither", - name="Dither (Palette)", - description="Apply one of a variety of dithering algorithms using colors from a given color palette. (Only the top row of pixels (y=0) of the palette will be used.)", - see_also="chainner:image:palette_from_image", - icon="MdShowChart", - inputs=[ - ImageInput(channels=[1, 3, 4]), - ImageInput(label="Palette", image_type=navi.Image(channels_as="Input0")), - EnumInput( - PaletteDitherAlgorithm, - option_labels=PALETTE_DITHER_ALGORITHM_LABELS, - default=PaletteDitherAlgorithm.DIFFUSION, - ).with_id(2), - if_enum_group(2, PaletteDitherAlgorithm.DIFFUSION)( - EnumInput( - ErrorDiffusionMap, - option_labels=ERROR_PROPAGATION_MAP_LABELS, - default=ErrorDiffusionMap.FLOYD_STEINBERG, - ).with_id(3), - ), - if_enum_group(2, PaletteDitherAlgorithm.RIEMERSMA)( - NumberInput( - "History Length", - minimum=2, - default=16, - ).with_id(4), - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def dither_palette_node( - img: np.ndarray, - palette: np.ndarray, - dither_algorithm: PaletteDitherAlgorithm, - error_diffusion_map: ErrorDiffusionMap, - history_length: int, -) -> np.ndarray: - assert ( - get_h_w_c(img)[2] == get_h_w_c(palette)[2] - ), "Image and palette must have the same number of channels." - - palette = palette[:1, ...] - quant = PaletteQuantization(palette) - - if dither_algorithm == PaletteDitherAlgorithm.NONE: - return quantize(img, quant) - elif dither_algorithm == PaletteDitherAlgorithm.DIFFUSION: - algorithm = DIFFUSION_ALGORITHM_MAP[error_diffusion_map] - return error_diffusion_dither(img, quant, algorithm) - elif dither_algorithm == PaletteDitherAlgorithm.RIEMERSMA: - return riemersma_dither( - img, - quant, - history_length, - 1 / history_length, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/quantize_to_reference.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/quantize_to_reference.py deleted file mode 100644 index c9d946d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/quantize/quantize_to_reference.py +++ /dev/null @@ -1,138 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import quantize_group - - -def add_xy(img: np.ndarray, scale: float) -> np.ndarray: - h, w, _ = get_h_w_c(img) - x = np.linspace(0, 1, w) - y = np.linspace(0, 1, h) - xx, yy = np.meshgrid(x, y) - return np.dstack((img, xx * scale, yy * scale)) - - -def quantize_image(image: np.ndarray, palette: np.ndarray): - _, _, c = get_h_w_c(image) - # Flatten image and palette for easy computation - flat_img = image.reshape((-1, c)) - flat_palette = palette.reshape((-1, c)) - - # For each pixel, find the nearest color in the palette - distances = np.linalg.norm(flat_img[:, np.newaxis] - flat_palette, axis=2) - closest_palette_idx = np.argmin(distances, axis=1) - quantized_img_flat = flat_palette[closest_palette_idx] - - # Reshape the quantized pixels to the original image shape - quantized_img = quantized_img_flat.reshape(image.shape) - - return quantized_img - - -@quantize_group.register( - schema_id="chainner:image:quantize_to_referece", - name="Quantize to Reference", - description=[ - "Quantize an image using another as a reference. Tries to preserve local color.", - "The main purpose of this node is to improve the upscaled images of pixel art. Upscaling models are typically not good at preserving color perfectly, and a smoothly upscaled image can look very different from the original.", - "Ideally, we would like to use the color palette of the original image to preserve the pixel art feel. While `chainner:image:palette_dither` can be used to this end, it will often choose colors from all over the original image. This is because upscaling models aren't very good at preserving color, and so the closest color in the palette may be very different from the color of that pixel in the original image.", - "This node addresses this issue using a **local color palette**. When quantizing a pixel in the upscaled image, we pick the nearest color from a small region around this pixel in the original image. This ensures that the quantized image will have the same colors in the roughly same positions as the original image.", - "#### Dithering", - "This node does not perform any dithering. If you want to dither the quantized image, use `chainner:image:palette_dither` on the target image before passing it into this node.", - ], - icon="BsPaletteFill", - inputs=[ - ImageInput("Target Image", channels=[3, 4]), - ImageInput( - "Reference Image", - channels=[3, 4], - image_type=navi.Image(channels_as="Input0"), - ), - SliderInput("Kernel Radius", minimum=1, maximum=5, default=1).with_docs( - "Determines the size of the region around each pixel in the reference image that is used to determine the local color palette.", - "The size of the region will be `2 * radius + 1`. So a radius of 1 will be a 3x3 region, a radius of 2 will be a 5x5 region, etc.", - ), - SliderInput( - "Spatial Weight", - minimum=0, - maximum=100, - precision=1, - default=0, - unit="%", - controls_step=1, - ).with_docs( - "When picking a color from the local color palette, this node not only considers the color but also the position of the pixel in the reference image. This value determines how much weight is given to the positions of the pixels in the local color palette. 0% means that the position is ignored, and 100% means that the position is the primary determining factor.", - """Which value is best depends on the image. E.g. 0% is best when the reference image contains dithering. Values >70% are typically not very useful.""", - ), - ], - outputs=[ - ImageOutput( - image_type=""" - if Input0.channels != Input1.channels { - error("The target image and reference image must have the same number of channels.") - } else if Input0.width < Input1.width or Input0.height < Input1.height { - error("The target image must be larger than the reference image.") - } else if number::mod(Input0.width, Input1.width) != 0 or number::mod(Input0.height, Input1.height) != 0 { - error("The size of the target image must be an integer multiple of the size of the reference image (e.g. 2x, 3x, 4x, 8x).") - } else { - Image { - width: max(Input0.width, Input1.width), - height: max(Input0.height, Input1.height), - channels: Input0.channels, - } - } - """, - assume_normalized=True, - ) - ], -) -def quantize_to_reference_node( - img: np.ndarray, - reference_img: np.ndarray, - kernel_radius: int, - spatial_scale: float, -) -> np.ndarray: - i_h, i_w, i_c = get_h_w_c(img) - r_h, r_w, r_c = get_h_w_c(reference_img) - assert i_c == r_c, "Image and reference image must have the same number of channels" - assert i_h >= r_h, "Image height must be larger than reference image height" - assert i_h % r_h == 0, "Image height must be a multiple of reference image height" - assert i_w >= r_w, "Image width must be larger than reference image width" - assert i_w % r_w == 0, "Image width must be a multiple of reference image width" - - spatial_scale = spatial_scale / 100 - spatial_scale = spatial_scale * spatial_scale - img = add_xy(img, r_w * spatial_scale) - reference_img = add_xy(reference_img, r_w * spatial_scale) - c = i_c + 2 - - kernel_size = 2 * kernel_radius + 1 - scale = i_h // r_h - - padded_ref = np.pad( - reference_img, - ((kernel_radius, kernel_radius), (kernel_radius, kernel_radius), (0, 0)), - mode="reflect", - ) - - result = np.zeros((i_h, i_w, i_c), dtype=np.float32) - - for h in range(r_h): - for w in range(r_w): - kernel = padded_ref[h : h + kernel_size, w : w + kernel_size] - colors = np.unique(kernel.reshape(-1, c), axis=0) - img_section = img[h * scale : (h + 1) * scale, w * scale : (w + 1) * scale] - quantized_section = quantize_image(img_section, colors) - quantized_section = quantized_section[:, :, :i_c] - result[ - h * scale : (h + 1) * scale, - w * scale : (w + 1) * scale, - ] = quantized_section - - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/sharpen/high_boost_filter.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/sharpen/high_boost_filter.py deleted file mode 100644 index b882e25..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/sharpen/high_boost_filter.py +++ /dev/null @@ -1,88 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.groups import if_enum_group -from nodes.impl.cas import cas_mix -from nodes.properties.inputs import BoolInput, EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import sharpen_group - - -class KernelType(Enum): - NORMAL = 0 - STRONG = 1 - - -@sharpen_group.register( - schema_id="chainner:image:sharpen_hbf", - name="High Boost Filter", - description="Apply sharpening to an image using a high boost filter.", - see_also="chainner:image:sharpen", - icon="MdBlurOff", - inputs=[ - ImageInput(), - EnumInput(KernelType, label="Filter Type"), - SliderInput( - "Amount", - minimum=0, - maximum=100, - default=2, - precision=1, - controls_step=1, - scale="log", - ), - BoolInput("Contrast Adaptive", default=False) - .with_id(3) - .with_docs( - "Enable contrast adaptive sharpening.", - "This will sharpen the image more evenly and prevents over-sharpening in dark areas and areas that are already quite sharp.", - ), - if_enum_group(3, 1)( - SliderInput( - "Contrast Bias", - minimum=1, - maximum=3, - default=2, - precision=2, - controls_step=0.1, - slider_step=0.1, - ).with_docs( - "A bias that controls the strength of the contrast adaptiveness. A bias of 2 is recommended, because it offers a good trade-off between sharpening and contrast adaptiveness.", - "A high bias will result in more sharpening but lose contrast adaptiveness. The bias is bounded between 1 and 3 because values higher than 3 effectively disable the contrast adaptiveness. A bias less than 2 will result in noticeable less sharpening, but apply that sharpening very evenly.", - ) - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def high_boost_filter_node( - img: np.ndarray, - kernel_type: KernelType, - amount: float, - contrast_adaptive: bool, - bias: float, -) -> np.ndarray: - if amount == 0: - return img - - identity = np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]]) - if kernel_type == KernelType.STRONG: - # 8-neighbor kernel - kernel = identity - np.array([[1, 1, 1], [1, 1, 1], [1, 1, 1]]) / 9 - else: - # 4-neighbor kernel - kernel = identity - np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]]) / 5 - - kernel = kernel * amount + identity - sharpened = cv2.filter2D(img, -1, kernel) - - if contrast_adaptive: - shape = cv2.MORPH_RECT if kernel_type == KernelType.STRONG else cv2.MORPH_CROSS - kernel = cv2.getStructuringElement(shape, (3, 3)) - sharpened = cas_mix(img, sharpened, kernel, bias) - - return sharpened diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/sharpen/unsharp_mask.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/sharpen/unsharp_mask.py deleted file mode 100644 index 5a6c58d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_filter/sharpen/unsharp_mask.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -import cv2 -import numpy as np - -from nodes.impl.image_utils import fast_gaussian_blur -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import sharpen_group - - -@sharpen_group.register( - schema_id="chainner:image:sharpen", - name="Unsharp Mask", - description="Apply sharpening to an image using an unsharp mask.", - see_also="chainner:image:sharpen_hbf", - icon="MdBlurOff", - inputs=[ - ImageInput(), - SliderInput( - "Radius", - minimum=0, - maximum=1000, - default=3, - precision=1, - controls_step=1, - slider_step=0.1, - scale="log", - ), - SliderInput( - "Amount", - minimum=0, - maximum=100, - default=1, - precision=1, - controls_step=1, - scale="log", - ), - SliderInput( - "Threshold", - minimum=0, - maximum=100, - default=0, - precision=1, - controls_step=1, - scale="log", - ), - ], - outputs=[ImageOutput(shape_as=0)], -) -def unsharp_mask_node( - img: np.ndarray, - radius: float, - amount: float, - threshold: float, -) -> np.ndarray: - if radius == 0 or amount == 0: - return img - - blurred = fast_gaussian_blur(img, radius) - - threshold /= 100 - if threshold == 0: - img = cv2.addWeighted(img, amount + 1, blurred, -amount, 0) - else: - diff = img - blurred - diff = np.sign(diff) * np.maximum(0, np.abs(diff) - threshold) - img = img + diff * amount - - return img diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/__init__.py deleted file mode 100644 index 0fa8bb4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from .. import image_utility_category - -modification_group = image_utility_category.add_node_group("Modification") -compositing_group = image_utility_category.add_node_group("Compositing") -miscellaneous_group = image_utility_category.add_node_group("Miscellaneous") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/add_caption.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/add_caption.py deleted file mode 100644 index e933868..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/add_caption.py +++ /dev/null @@ -1,46 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from nodes.impl.caption import CaptionPosition, add_caption -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput, TextInput -from nodes.properties.outputs import ImageOutput - -from .. import compositing_group - - -@compositing_group.register( - schema_id="chainner:image:caption", - name="Add Caption", - description="Add a caption to the top or bottom of an image.", - icon="MdVideoLabel", - inputs=[ - ImageInput(), - TextInput("Caption"), - NumberInput("Size", minimum=20, default=42, unit="px"), - EnumInput( - CaptionPosition, - "Position", - default=CaptionPosition.BOTTOM, - label_style="inline", - ), - ], - outputs=[ - ImageOutput( - image_type=""" - // this value is defined by `add_caption` - let captionHeight = Input2; - Image { - width: Input0.width, - height: Input0.height + captionHeight, - channels: Input0.channels, - } - """, - assume_normalized=True, - ) - ], -) -def add_caption_node( - img: np.ndarray, caption: str, size: int, position: CaptionPosition -) -> np.ndarray: - return add_caption(img, caption, size, position) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/blend_images.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/blend_images.py deleted file mode 100644 index fdcea17..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/blend_images.py +++ /dev/null @@ -1,292 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.groups import Condition, if_enum_group, if_group -from nodes.impl.blend import BlendMode, blend_images -from nodes.impl.color.color import Color -from nodes.impl.image_utils import as_2d_grayscale -from nodes.impl.pil_utils import convert_to_bgra -from nodes.properties.inputs import ( - BlendModeDropdown, - BoolInput, - EnumInput, - ImageInput, - NumberInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import compositing_group - - -class BlendOverlayPosition(Enum): - TOP_LEFT = "top_left" - TOP_CENTERED = "top_centered" - TOP_RIGHT = "top_right" - CENTERED_LEFT = "centered_left" - CENTERED = "centered" - CENTERED_RIGHT = "centered_right" - BOTTOM_LEFT = "bottom_left" - BOTTOM_CENTERED = "bottom_centered" - BOTTOM_RIGHT = "bottom_right" - PERCENT_OFFSET = "percent_offset" - PIXEL_OFFSET = "pixel_offset" - - -BLEND_OVERLAY_POSITION_LABELS = { - BlendOverlayPosition.TOP_LEFT: "Top left", - BlendOverlayPosition.TOP_CENTERED: "Top centered", - BlendOverlayPosition.TOP_RIGHT: "Top right", - BlendOverlayPosition.CENTERED_LEFT: "Centered left", - BlendOverlayPosition.CENTERED: "Centered", - BlendOverlayPosition.CENTERED_RIGHT: "Centered right", - BlendOverlayPosition.BOTTOM_LEFT: "Bottom left", - BlendOverlayPosition.BOTTOM_CENTERED: "Bottom centered", - BlendOverlayPosition.BOTTOM_RIGHT: "Bottom right", - BlendOverlayPosition.PERCENT_OFFSET: "Offset (%)", - BlendOverlayPosition.PIXEL_OFFSET: "Offset (pixels)", -} - -BLEND_OVERLAY_X0_Y0_FACTORS = { - BlendOverlayPosition.TOP_LEFT: np.array([0, 0]), - BlendOverlayPosition.TOP_CENTERED: np.array([0.5, 0]), - BlendOverlayPosition.TOP_RIGHT: np.array([1, 0]), - BlendOverlayPosition.CENTERED_LEFT: np.array([0, 0.5]), - BlendOverlayPosition.CENTERED: np.array([0.5, 0.5]), - BlendOverlayPosition.CENTERED_RIGHT: np.array([1, 0.5]), - BlendOverlayPosition.BOTTOM_LEFT: np.array([0, 1]), - BlendOverlayPosition.BOTTOM_CENTERED: np.array([0.5, 1]), - BlendOverlayPosition.BOTTOM_RIGHT: np.array([1, 1]), - BlendOverlayPosition.PERCENT_OFFSET: np.array([1, 1]), - BlendOverlayPosition.PIXEL_OFFSET: np.array([0, 0]), -} - - -@compositing_group.register( - schema_id="chainner:image:blend", - name="Blend Images", - description="""Blends an overlay image onto a base image using the specified mode.""", - icon="BsLayersHalf", - inputs=[ - ImageInput("Base Layer", channels=[1, 3, 4], allow_colors=True), - ImageInput("Overlay Layer", channels=[1, 3, 4], allow_colors=True), - BlendModeDropdown(), - if_group(Condition.type(0, "Image") & Condition.type(1, "Image"))( - EnumInput( - BlendOverlayPosition, - label="Overlay position", - option_labels=BLEND_OVERLAY_POSITION_LABELS, - default=BlendOverlayPosition.CENTERED, - ), - if_enum_group(3, (BlendOverlayPosition.PERCENT_OFFSET))( - SliderInput( - "X offset", - precision=0, - controls_step=1, - minimum=-200, - maximum=200, - default=0, - unit="%", - ), - SliderInput( - "Y offset", - precision=0, - controls_step=1, - minimum=-200, - maximum=200, - default=0, - unit="%", - ), - ), - if_enum_group(3, (BlendOverlayPosition.PIXEL_OFFSET))( - NumberInput( - "X offset", - controls_step=1, - minimum=None, - maximum=None, - default=0, - unit="px", - ), - NumberInput( - "Y offset", - controls_step=1, - minimum=None, - maximum=None, - default=0, - unit="px", - ), - ), - BoolInput("Crop to fit base layer", default=False), - ), - ], - outputs=[ - ImageOutput( - image_type=""" - let base = Input0; - let overlay = Input1; - let position:BlendOverlayPosition = Input3; - let cropToFit = bothImages and Input8; - - def isImage(x: any) = match x { Image => true, _ => false }; - let bothImages = isImage(base) and isImage(overlay); - - def getWidth(img: any) = match img { Image => img.width, _ => -inf }; - def getHeight(img: any) = match img { Image => img.height, _ => -inf }; - - struct Size { width: uint, height: uint} - def imageToSize(x: any): Size = Size { - width: getWidth(x) & uint, - height: getHeight(x) & uint - }; - let baseSize:Size = imageToSize(Input0); - let overlaySize:Size = imageToSize(Input1); - let maxSize:Size = Size { - width: max(getWidth(Input0), getWidth(Input1)) & uint, - height: max(getHeight(Input0), getHeight(Input1)) & uint, - }; - - def getExtendedDim(base_dim: uint, ov_dim: uint, offset: int): uint { - abs(min(offset, 0)) + max(base_dim, (offset + ov_dim)) - } - def getExtendedCanvasSize(b: Size, o: Size, x_offset: int, y_offset: int): Size { - Size { - width: getExtendedDim(b.width, o.width, x_offset), - height: getExtendedDim(b.height, o.height, y_offset), - } - } - def percentToOffset(b: uint, o: uint, percent: int): int { - round(((b - o) * percent / 100.0)) & int - } - let extendedCanvasSize = match position { - BlendOverlayPosition::PercentOffset => getExtendedCanvasSize( - baseSize, - overlaySize, - percentToOffset(baseSize.width, overlaySize.width, Input4), - percentToOffset(baseSize.height, overlaySize.height, Input5), - ), - BlendOverlayPosition::PixelOffset => getExtendedCanvasSize( - baseSize, overlaySize, Input6, Input7 - ), - _ => maxSize - }; - - let canvasSize = if cropToFit { - baseSize - } else { - if bothImages {extendedCanvasSize} else {maxSize} - }; - - Image { - width: canvasSize.width & uint, - height: canvasSize.height & uint, - channels: max(base.channels, overlay.channels) - } - """, - assume_normalized=True, - ).with_never_reason("At least one layer must be an image"), - ], -) -def blend_images_node( - base: np.ndarray | Color, - ov: np.ndarray | Color, - blend_mode: BlendMode, - overlay_position: BlendOverlayPosition, - x_percent: int, - y_percent: int, - x_px: int, - y_px: int, - crop_to_fit: bool, -) -> np.ndarray: - # Convert colors to images - do_crop_to_fit = crop_to_fit - if isinstance(base, Color): - if isinstance(ov, Color): - raise ValueError("At least one layer must be an image") - base = base.to_image(width=ov.shape[1], height=ov.shape[0]) - do_crop_to_fit = False - if isinstance(ov, Color): - ov = ov.to_image(width=base.shape[1], height=base.shape[0]) - do_crop_to_fit = False - - base_height, base_width, base_channel_count = get_h_w_c(base) - overlay_height, overlay_width, _ = get_h_w_c(ov) - - # Calculate coordinates of the overlay layer - # origin: top-let point of the base layer - # (x0, y0): top-left point of the overlay layer - # (x1, y1): bottom-right point of the overlay layer - if overlay_position == BlendOverlayPosition.PERCENT_OFFSET: - x0, y0 = [ - round((base_width - overlay_width) * x_percent / 100), - round((base_height - overlay_height) * y_percent / 100), - ] - elif overlay_position == BlendOverlayPosition.PIXEL_OFFSET: - x0, y0 = [x_px, y_px] - else: - x0, y0 = np.array( - [base_width - overlay_width, base_height - overlay_height] - * BLEND_OVERLAY_X0_Y0_FACTORS[overlay_position] - ).astype("int") - x1, y1 = x0 + overlay_width, y0 + overlay_height - - # Add borders to the base layer - top = bottom = left = right = 0 - if not do_crop_to_fit: - left, right = abs(min(x0, 0)), max(0, (x1 - base_width)) - top, bottom = abs(min(y0, 0)), max(0, (y1 - base_height)) - - if any((top, bottom, left, right)): - # copyMakeBorder will create black border if base not converted to RGBA first - base = convert_to_bgra(base, base_channel_count) - base = cv2.copyMakeBorder( - base, top, bottom, left, right, cv2.BORDER_CONSTANT, value=(0.0,) - ) - assert isinstance(base, np.ndarray) - else: # Make sure cached image not being worked on regardless - base = base.copy() - - # Coordinates of the intersection area - if do_crop_to_fit: - i_x0, i_x1 = max(0, x0), min(x1, base_width) - i_y0, i_y1 = max(0, y0), min(y1, base_height) - if not (0 <= i_x0 < i_x1 and 0 <= i_y0 < i_y1): - return base - - ov_x0 = max(0, -1 * x0) - ov_x1 = ov_x0 + i_x1 - i_x0 - ov_y0 = max(0, -1 * y0) - ov_y1 = ov_y0 + i_y1 - i_y0 - # Crop overlay - ov = ov[ - ov_y0:ov_y1, - ov_x0:ov_x1, - ] - else: - i_x0, i_x1 = x0 + left, x1 + left - i_y0, i_y1 = y0 + top, y1 + top - - # Blend layers - blended_img = blend_images( - ov, - base[i_y0:i_y1, i_x0:i_x1], - blend_mode, - ) - - result = base # Just so the names make sense - result_c = get_h_w_c(result)[2] - blend_c = get_h_w_c(blended_img)[2] - - # Have to ensure blend and result have same shape - if result_c < blend_c: - if blend_c == 4: - result = convert_to_bgra(result, result_c) - else: - result = as_2d_grayscale(result) - result = np.dstack((result, result, result)) - result[i_y0:i_y1, i_x0:i_x1] = blended_img - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/stack_images.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/stack_images.py deleted file mode 100644 index 7d53242..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/stack_images.py +++ /dev/null @@ -1,225 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -from nodes.groups import optional_list_group -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import ALPHABET, get_h_w_c, round_half_up - -from .. import compositing_group - - -class Orientation(Enum): - HORIZONTAL = "horizontal" - VERTICAL = "vertical" - - -@compositing_group.register( - schema_id="chainner:image:stack", - name="Stack Images", - description="Concatenate (stack) multiple images horizontally or vertically.", - icon="CgMergeVertical", - inputs=[ - EnumInput(Orientation).with_id(4), - ImageInput("Image A").with_id(0), - ImageInput("Image B").make_optional().with_id(1), - optional_list_group( - ImageInput("Image C").make_optional().with_id(2), - ImageInput("Image D").make_optional().with_id(3), - *[ - ImageInput(f"Image {letter}").make_optional() - for letter in ALPHABET[4:14] - ], - ), - ], - outputs=[ - ImageOutput( - image_type=""" - def getWidth(img: Image | null) = match img { null => 0, _ as i => i.width }; - def getHeight(img: Image | null) = match img { null => 0, _ as i => i.height }; - def getChannels(img: Image | null) { - match img { - null => 0, - _ as i => match i.channels { 1 => 3, _ as c => c } - } - } - - let maxWidth = max( - Input0.width, - getWidth(Input1), - getWidth(Input2), - getWidth(Input3), - getWidth(Input5), - getWidth(Input6), - getWidth(Input7), - getWidth(Input8), - getWidth(Input9), - getWidth(Input10), - getWidth(Input11), - getWidth(Input12), - getWidth(Input13), - getWidth(Input14) - ); - let maxHeight = max( - Input0.height, - getHeight(Input1), - getHeight(Input2), - getHeight(Input3), - getHeight(Input5), - getHeight(Input6), - getHeight(Input7), - getHeight(Input8), - getHeight(Input9), - getHeight(Input10), - getHeight(Input11), - getHeight(Input12), - getHeight(Input13), - getHeight(Input14) - ); - let maxChannels = max( - Input0.channels, - getChannels(Input1), - getChannels(Input2), - getChannels(Input3), - getChannels(Input5), - getChannels(Input6), - getChannels(Input7), - getChannels(Input8), - getChannels(Input9), - getChannels(Input10), - getChannels(Input11), - getChannels(Input12), - getChannels(Input13), - getChannels(Input14) - ); - - def getAdjustedWidth(img: Image | null) { - match img { - null => 0, - _ as i => int(1..) & round(i.width * maxHeight / i.height) - } - } - def getAdjustedHeight(img: Image | null) { - match img { - null => 0, - _ as i => int(1..) & round(i.height * maxWidth / i.width) - } - } - - let widthSum = - getAdjustedWidth(Input0) - + getAdjustedWidth(Input1) - + getAdjustedWidth(Input2) - + getAdjustedWidth(Input3) - + getAdjustedWidth(Input5) - + getAdjustedWidth(Input6) - + getAdjustedWidth(Input7) - + getAdjustedWidth(Input8) - + getAdjustedWidth(Input9) - + getAdjustedWidth(Input10) - + getAdjustedWidth(Input11) - + getAdjustedWidth(Input12) - + getAdjustedWidth(Input13) - + getAdjustedWidth(Input14); - let heightSum = - getAdjustedHeight(Input0) - + getAdjustedHeight(Input1) - + getAdjustedHeight(Input2) - + getAdjustedHeight(Input3) - + getAdjustedHeight(Input5) - + getAdjustedHeight(Input6) - + getAdjustedHeight(Input7) - + getAdjustedHeight(Input8) - + getAdjustedHeight(Input9) - + getAdjustedHeight(Input10) - + getAdjustedHeight(Input11) - + getAdjustedHeight(Input12) - + getAdjustedHeight(Input13) - + getAdjustedHeight(Input14); - - Image { - width: match Input4 { - Orientation::Vertical => maxWidth, - Orientation::Horizontal => widthSum - }, - height: match Input4 { - Orientation::Vertical => heightSum, - Orientation::Horizontal => maxHeight - }, - channels: maxChannels - } - """ - ) - ], -) -def stack_images_node( - orientation: Orientation, - image_a: np.ndarray, - *other_images: np.ndarray | None, -) -> np.ndarray: - imgs = [] - max_h, max_w, max_c = 0, 0, 1 - for img in [image_a, *other_images]: - if img is not None: - h, w, c = get_h_w_c(img) - if c == 1: - img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) # noqa - c = 3 - max_h = max(h, max_h) - max_w = max(w, max_w) - max_c = max(c, max_c) - imgs.append(img) - - fixed_imgs: list[np.ndarray] = [] - for img in imgs: - h, w, c = get_h_w_c(img) - - fixed_img = img - # Fix images so they resize proportionally to the max image - if orientation == Orientation.HORIZONTAL: - if h < max_h: - fixed_img = cv2.resize( - img, - (round_half_up(w * max_h / h), max_h), - interpolation=cv2.INTER_NEAREST, - ) - elif orientation == Orientation.VERTICAL: - if w < max_w: - fixed_img = cv2.resize( - img, - (max_w, round_half_up(h * max_w / w)), - interpolation=cv2.INTER_NEAREST, - ) - else: - raise AssertionError(f"Invalid orientation '{orientation}'") - - # Expand channel dims if necessary - if c < max_c: - temp_img = np.ones((max_h, max_w, max_c), dtype=np.float32) - temp_img[:, :, :c] = fixed_img - fixed_img = temp_img - - fixed_imgs.append(fixed_img.astype("float32")) - - if orientation == Orientation.HORIZONTAL: - for i in range(len(fixed_imgs)): - assert ( - fixed_imgs[i].shape[0] == fixed_imgs[0].shape[0] - ), "Inputted heights are not the same and could not be auto-fixed" - assert ( - fixed_imgs[i].dtype == fixed_imgs[0].dtype - ), "The image types are not the same and could not be auto-fixed" - return cv2.hconcat(fixed_imgs) - elif orientation == Orientation.VERTICAL: - for i in range(len(fixed_imgs)): - assert ( - fixed_imgs[i].shape[1] == fixed_imgs[0].shape[1] - ), "Inputted widths are not the same and could not be auto-fixed" - assert ( - fixed_imgs[i].dtype == fixed_imgs[0].dtype - ), "The image types are not the same and could not be auto-fixed" - return cv2.vconcat(fixed_imgs) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/z_stack_images.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/z_stack_images.py deleted file mode 100644 index 8ef5685..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/compositing/z_stack_images.py +++ /dev/null @@ -1,86 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import optional_list_group -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import ALPHABET, get_h_w_c - -from .. import compositing_group - - -class Expression(Enum): - MEDIAN = "median" - MEAN = "mean" - MIN = "minimum" - MAX = "maximum" - - -@compositing_group.register( - schema_id="chainner:image:z_stack", - name="Z-Stack Images", - description="""Aligns multiple images and evaluates them in relation to each other to create a merged image result.""", - icon="BsLayersHalf", - inputs=[ - EnumInput(Expression), - ImageInput("Image A"), - ImageInput("Image B"), - optional_list_group( - *[ - ImageInput(f"Image {letter}").make_optional() - for letter in ALPHABET[2:14] - ], - ), - ], - outputs=[ - ImageOutput( - image_type=""" - def conv(i: Image | null) = match i { Image => i, _ => any }; - - Input1 & Input2 - & conv(Input3) - & conv(Input4) - & conv(Input5) - & conv(Input6) - & conv(Input7) - & conv(Input8) - & conv(Input9) - & conv(Input10) - & conv(Input11) - & conv(Input12) - & conv(Input13) - & conv(Input14) - """ - ).with_never_reason( - "All input images much have the same size and number of channels." - ), - ], -) -def z_stack_images_node( - expression: Expression, - *inputs: np.ndarray | None, -) -> np.ndarray: - images = [x for x in inputs if x is not None] - assert ( - 2 <= len(images) <= 15 - ), f"Number of images must be between 2 and 15 ({len(images)})" - - assert all( - get_h_w_c(image) == get_h_w_c(images[0]) for image in images - ), "All images must have the same dimensions and channels" - - if expression == Expression.MEAN: - result = np.mean(images, axis=0) - elif expression == Expression.MEDIAN: - result = np.median(images, axis=0) - elif expression == Expression.MIN: - result = np.min(images, axis=0) - elif expression == Expression.MAX: - result = np.max(images, axis=0) - else: - raise AssertionError(f"Invalid expression '{expression}'") - - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/apply_palette.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/apply_palette.py deleted file mode 100644 index f8c30d2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/apply_palette.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import miscellaneous_group - - -def quantize(img: np.ndarray, levels: int) -> np.ndarray: - """ - Given an array for float32 values between 0 and 1, this will return a quantized version of the array with the given number of quantization levels. - - The type of the integers in the returned array will be the smallest unsigned integer type that can fit `levels` many values. E.g. uint8 is used for 256, and uint16 is used for 257 levels. - """ - assert levels >= 1 - assert ( - levels <= 2**24 - ), "Quantizing float32 values with more than 2**24 levels doesn't make sense, because only integers up to 2**24 can be represented exactly using float32." - - q: np.ndarray = np.round(img * (levels - 1)) - - if levels <= 256: - return q.astype(np.uint8) - elif levels <= 65536: - return q.astype(np.uint16) - else: - return q.astype(np.uint32) - - -@miscellaneous_group.register( - schema_id="chainner:image:lut", - name="Apply Palette", - description=( - "Apply a color palette to a grayscale image." - " Only the top row of pixels (y=0) of the palette will be used to do the look up." - ), - see_also="chainner:image:palette_from_image", - icon="MdGradient", - inputs=[ - ImageInput(channels=1), - ImageInput("Palette"), - ], - outputs=[ - ImageOutput(image_type=navi.Image(size_as="Input0", channels_as="Input1")) - ], -) -def apply_palette_node( - img: np.ndarray, - lut: np.ndarray, -) -> np.ndarray: - # convert to the size of the LUT - _, w, _ = get_h_w_c(lut) - img = quantize(img, w) - - # only use top row of lut - return np.take(lut[0], img, axis=0) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/change_color_model.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/change_color_model.py deleted file mode 100644 index b944330..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/change_color_model.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.groups import from_to_dropdowns_group, if_enum_group -from nodes.impl.color.convert import ( - color_space_from_id, - color_space_or_detector_from_id, - convert, -) -from nodes.impl.color.convert_data import color_spaces, get_alpha_partner -from nodes.properties.inputs import ( - BoolInput, - ColorSpaceDetectorInput, - ColorSpaceInput, - ImageInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - -COLOR_SPACES_WITH_ALPHA_PARTNER = [ - c.id for c in color_spaces if get_alpha_partner(c) is not None -] - - -@miscellaneous_group.register( - schema_id="chainner:image:change_colorspace", - name="Change Color Model", - description=( - "Convert the color model of an image to a different one. " - "Also can convert to different channel-spaces." - ), - icon="MdColorLens", - inputs=[ - ImageInput(image_type=navi.Image(channels="Input1.channels")), - from_to_dropdowns_group( - ColorSpaceDetectorInput(label="From").with_id(1), - ColorSpaceInput(label="To").with_id(2), - ), - if_enum_group(2, COLOR_SPACES_WITH_ALPHA_PARTNER)( - BoolInput("Output Alpha", default=False), - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - size_as="Input0", - channels=""" - if Input2.supportsAlpha and Input3 { - 4 - } else { - Input2.channels - } - """, - ), - assume_normalized=True, - ) - ], -) -def change_color_model_node( - img: np.ndarray, input_: int, output: int, alpha: bool -) -> np.ndarray: - from_cs = color_space_or_detector_from_id(input_) - to_cs = color_space_from_id(output) - - alpha_cs = get_alpha_partner(to_cs) - if alpha and alpha_cs is not None: - assert alpha_cs.channels == 4 - to_cs = alpha_cs - - return convert(img, from_cs, to_cs) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/change_colorspace.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/change_colorspace.py deleted file mode 100644 index 82e8292..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/change_colorspace.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.groups import from_to_dropdowns_group, if_enum_group -from nodes.impl.color.convert import ( - color_space_from_id, - color_space_or_detector_from_id, - convert, -) -from nodes.impl.color.convert_data import color_spaces, get_alpha_partner -from nodes.properties.inputs import ( - BoolInput, - ColorSpaceDetectorInput, - ColorSpaceInput, - ImageInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - -COLOR_SPACES_WITH_ALPHA_PARTNER = [ - c.id for c in color_spaces if get_alpha_partner(c) is not None -] - - -@miscellaneous_group.register( - schema_id="chainner:image:change_colorspace", - name="Change Colorspace", - description=( - "Convert the colorspace of an image to a different one. " - "Also can convert to different channel-spaces." - ), - icon="MdColorLens", - inputs=[ - ImageInput(image_type=navi.Image(channels="Input1.channels")), - from_to_dropdowns_group( - ColorSpaceDetectorInput(label="From").with_id(1), - ColorSpaceInput(label="To").with_id(2), - ), - if_enum_group(2, COLOR_SPACES_WITH_ALPHA_PARTNER)( - BoolInput("Output Alpha", default=False), - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - size_as="Input0", - channels=""" - if bool::and(Input2.supportsAlpha, Input3) { - 4 - } else { - Input2.channels - } - """, - ), - assume_normalized=True, - ) - ], -) -def change_colorspace_node( - img: np.ndarray, input_: int, output: int, alpha: bool -) -> np.ndarray: - from_cs = color_space_or_detector_from_id(input_) - to_cs = color_space_from_id(output) - - alpha_cs = get_alpha_partner(to_cs) - if alpha and alpha_cs is not None: - assert alpha_cs.channels == 4 - to_cs = alpha_cs - - return convert(img, from_cs, to_cs) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/generate_hash.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/generate_hash.py deleted file mode 100644 index 11b0477..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/generate_hash.py +++ /dev/null @@ -1,41 +0,0 @@ -from __future__ import annotations - -import base64 -import hashlib - -import numpy as np - -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import TextOutput - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:generate_hash", - name="Generate Hash", - description="Generate a hash from an image using the BLAKE2b hashing algorithm.", - icon="MdCalculate", - inputs=[ - ImageInput(), - SliderInput( - "Digest Size (in bytes)", - minimum=1, - maximum=64, - default=8, - precision=0, - controls_step=1, - ).with_docs( - "The digest size determines the length of the hash that is returned." - ), - ], - outputs=[ - TextOutput("Hex"), - TextOutput("Base64"), - ], -) -def generate_hash_node(img: np.ndarray, size: int) -> tuple[str, str]: - img = np.ascontiguousarray(to_uint8(img)) - h = hashlib.blake2b(img, digest_size=size) # type: ignore - return h.hexdigest(), base64.urlsafe_b64encode(h.digest()).decode("utf-8") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/image_metrics.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/image_metrics.py deleted file mode 100644 index 4ed0415..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/image_metrics.py +++ /dev/null @@ -1,51 +0,0 @@ -from __future__ import annotations - -import math - -import cv2 -import numpy as np - -from nodes.impl.image_utils import calculate_ssim -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.utils import get_h_w_c - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:image_metrics", - name="Image Metrics", - description=( - """Calculate image quality metrics (MSE, PSNR, SSIM) between two images.""" - ), - icon="MdOutlineAssessment", - inputs=[ - ImageInput("Original Image"), - ImageInput("Comparison Image"), - ], - outputs=[ - NumberOutput("MSE", output_type="0..1"), - NumberOutput("PSNR", output_type="0.."), - NumberOutput("SSIM", output_type="0..1"), - ], -) -def image_metrics_node( - orig_img: np.ndarray, comp_img: np.ndarray -) -> tuple[float, float, float]: - assert ( - orig_img.shape == comp_img.shape - ), "Images must have same dimensions and color depth" - - # If an image is not grayscale, convert to YCrCb and compute metrics - # on luma channel only - c = get_h_w_c(orig_img)[2] - if c > 1: - orig_img = cv2.cvtColor(orig_img, cv2.COLOR_BGR2YCrCb)[:, :, 0] - comp_img = cv2.cvtColor(comp_img, cv2.COLOR_BGR2YCrCb)[:, :, 0] - - mse = round(np.mean((comp_img - orig_img) ** 2), 6) # type: ignore - psnr = round(10 * math.log(1 / mse), 6) - ssim = round(calculate_ssim(comp_img, orig_img), 6) - - return (float(mse), float(psnr), ssim) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/image_statistics.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/image_statistics.py deleted file mode 100644 index 419ef76..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/image_statistics.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -from typing import SupportsFloat - -import numpy as np - -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import NumberOutput - -from .. import miscellaneous_group - - -@miscellaneous_group.register( - schema_id="chainner:image:image_statistics", - name="Image Statistics", - description="Returns statistics of the given image.", - icon="MdOutlineAssessment", - inputs=[ - ImageInput(channels=1), - SliderInput( - "Percentile", - precision=2, - minimum=0, - maximum=100, - default=50, - slider_step=1, - controls_step=1, - hide_trailing_zeros=True, - ), - ], - outputs=[ - NumberOutput("Minimum", output_type="0..255"), - NumberOutput("Maximum", output_type="0..255"), - NumberOutput("Arithmetic Mean", output_type="0..255"), - NumberOutput("Percentile", output_type="0..255"), - ], -) -def image_statistics_node( - img: np.ndarray, - percentile: float, -) -> tuple[float, float, float, float]: - def to_float(n: SupportsFloat) -> float: - # float32 has ~8 digits of precision. - # So by rounding to 4 digits, we have 1 digit left over to contain rounding errors - return round(float(n) * 255, 4) - - return ( - to_float(np.min(img)), - to_float(np.max(img)), - to_float(np.mean(img)), - to_float(np.percentile(img, percentile)), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/inpaint.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/inpaint.py deleted file mode 100644 index 6a360db..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/inpaint.py +++ /dev/null @@ -1,74 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import cv2 -import numpy as np - -import navi -from nodes.impl.image_utils import to_uint8 -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -class InpaintAlgorithm(Enum): - NS = cv2.INPAINT_NS - TELEA = cv2.INPAINT_TELEA - - -@miscellaneous_group.register( - schema_id="chainner:image:inpaint", - name="Inpaint", - description=[ - "Inpaint an image with given mask.", - "Masks must typically be made outside of chaiNNer.", - ], - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput(channels=[1, 3]), - ImageInput(label="Mask", channels=1).with_docs( - "An inpainting mask is a grayscale image where white represents what to inpaint and black represents what to keep.", - "This must typically be made outside of chaiNNer.", - hint=True, - ), - EnumInput( - InpaintAlgorithm, - option_labels={ - InpaintAlgorithm.NS: "Navier Stokes", - InpaintAlgorithm.TELEA: "Telea", - }, - ), - NumberInput( - "Search Radius", - minimum=0, - default=1, - precision=1, - controls_step=1, - ), - ], - outputs=[ - ImageOutput( - image_type=navi.Image( - width="Input0.width & Input1.width", - height="Input0.height & Input1.height", - channels="Input0.channels", - ) - ).with_never_reason("The given image and mask must have the same resolution.") - ], - limited_to_8bpc=True, -) -def inpaint_node( - img: np.ndarray, - mask: np.ndarray, - inpaint_method: InpaintAlgorithm, - radius: float, -) -> np.ndarray: - assert ( - img.shape[:2] == mask.shape[:2] - ), "Input image and mask must have the same resolution" - - img = to_uint8(img, normalized=True) - mask = to_uint8(mask, normalized=True) - return cv2.inpaint(img, mask, radius, inpaint_method.value) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/palette_from_image.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/palette_from_image.py deleted file mode 100644 index f5b86f9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/palette_from_image.py +++ /dev/null @@ -1,105 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -import navi -from nodes.groups import if_enum_group -from nodes.impl.dithering.palette import ( - distinct_colors_palette, - kmeans_palette, - median_cut_palette, -) -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput -from nodes.properties.outputs import ImageOutput - -from .. import miscellaneous_group - - -class PaletteExtractionMethod(Enum): - ALL = "all" - KMEANS = "k-means" - MEDIAN_CUT = "median" - - -PALETTE_EXTRACTION_METHOD_LABELS = { - PaletteExtractionMethod.ALL: "All distinct colors", - PaletteExtractionMethod.KMEANS: "K-Means", - PaletteExtractionMethod.MEDIAN_CUT: "Median cut", -} - -MAX_COLORS = 4096 - - -@miscellaneous_group.register( - schema_id="chainner:image:palette_from_image", - name="Palette from Image", - description=[ - "Use an image to create a color palette.", - "The color palette is returned as an image with one row (height=1). All colors of the palette are in the top row of the image.", - f'*Note:* The "{PALETTE_EXTRACTION_METHOD_LABELS[PaletteExtractionMethod.ALL]}" option only supports images with at most {MAX_COLORS} distinct colors. If the image has more colors, an error will occur.', - ], - see_also=[ - "chainner:image:lut", - "chainner:image:palette_dither", - ], - icon="MdGradient", - inputs=[ - ImageInput(), - EnumInput( - PaletteExtractionMethod, - option_labels=PALETTE_EXTRACTION_METHOD_LABELS, - default=PaletteExtractionMethod.KMEANS, - ).with_id(1), - if_enum_group( - 1, - (PaletteExtractionMethod.KMEANS, PaletteExtractionMethod.MEDIAN_CUT), - )( - NumberInput( - "Palette Size", minimum=2, maximum=MAX_COLORS, default=8 - ).with_id(2), - ), - ], - outputs=[ - ImageOutput( - "Palette", - image_type=navi.Image( - width=""" - min( - match Input1 { - PaletteExtractionMethod::All => int(1..), - _ => Input2 - }, - MAX_COLORS - ) - """.replace("MAX_COLORS", str(MAX_COLORS)), - height=1, - channels_as="Input0", - ), - ) - ], -) -def palette_from_image_node( - img: np.ndarray, - palette_extraction_method: PaletteExtractionMethod, - palette_size: int, -) -> np.ndarray: - distinct_colors = distinct_colors_palette(img) - distinct_count = distinct_colors.shape[1] - - if palette_extraction_method == PaletteExtractionMethod.ALL: - if distinct_count > MAX_COLORS: - raise ValueError( - f"Image has {distinct_count} distinct colors, but only palettes with at most {MAX_COLORS} colors are supported." - ) - return distinct_colors - - if palette_size >= distinct_count: - excess = palette_size - distinct_count - return np.pad(distinct_colors, [(0, 0), (0, excess), (0, 0)], mode="edge") # type: ignore - - if palette_extraction_method == PaletteExtractionMethod.KMEANS: - return kmeans_palette(img, palette_size) - elif palette_extraction_method == PaletteExtractionMethod.MEDIAN_CUT: - return median_cut_palette(img, palette_size) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/pick_color.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/pick_color.py deleted file mode 100644 index 948c9ca..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/miscellaneous/pick_color.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import if_enum_group -from nodes.impl.color.color import Color -from nodes.properties.inputs import EnumInput, ImageInput, NumberInput, SliderInput -from nodes.properties.outputs import ColorOutput -from nodes.utils.utils import get_h_w_c, round_half_up - -from .. import miscellaneous_group - - -class CoordinateType(Enum): - RELATIVE = 1 - ABSOLUTE = 2 - - -@miscellaneous_group.register( - schema_id="chainner:image:pick_color", - name="Pick Color", - description=[ - "Returns the color of the pixel at the given coordinate.", - "This node supports 2 coordinate types:", - "- Relative: The coordinates are relative to the image size. So (X=0, Y=0) is the top-left pixel, ((X=50%, Y=50%) is the center pixel, and (X=100%, Y=100%) is the bottom-right pixel.", - "- Absolute: The coordinates are absolute pixel coordinates. So (X=0, Y=0) is the top-left pixel and (X=width-1, Y=height-1) is the bottom-right pixel. X-Y coordinates outside the image are not allowed and will result in an error.", - ], - icon="MdColorize", - inputs=[ - ImageInput(channels=[1, 3, 4]).with_id(0), - EnumInput( - CoordinateType, default=CoordinateType.ABSOLUTE, preferred_style="tabs" - ).with_id(3), - if_enum_group(3, CoordinateType.RELATIVE)( - SliderInput( - "X", - maximum=100, - default=0, - controls_step=1, - unit="%", - ) - .with_docs("Relative X coordinate.") - .with_id(4), - SliderInput( - "Y", - maximum=100, - default=0, - controls_step=1, - unit="%", - ) - .with_docs("Relative Y coordinate.") - .with_id(5), - ), - if_enum_group(3, CoordinateType.ABSOLUTE)( - NumberInput( - "X", - default=0, - minimum=0, - unit="px", - ) - .with_docs("Absolute X coordinate.") - .with_id(1), - NumberInput( - "Y", - default=0, - minimum=0, - unit="px", - ) - .with_docs("Absolute Y coordinate.") - .with_id(2), - ), - ], - outputs=[ - ColorOutput( - "Color", - color_type=""" - let image = Input0; - let coords = Input3; - let x = Input1; - let y = Input2; - - let valid = match coords { - CoordinateType::Relative => true, - CoordinateType::Absolute => x < image.width and y < image.height, - }; - - if valid { - Color { channels: Input0.channels } - } else { - never - } - """, - ).with_never_reason("The given coordinates (X, Y) are outside the image."), - ], -) -def pick_color_node( - orig_img: np.ndarray, - coord_type: CoordinateType, - x_rel: int, - y_rel: int, - x: int, - y: int, -) -> Color: - h, w, c = get_h_w_c(orig_img) - - if coord_type == CoordinateType.RELATIVE: - x = round_half_up((w - 1) * x_rel / 100) - y = round_half_up((h - 1) * y_rel / 100) - - assert x < w and y < h, ( - "The given coordinates (X, Y) are outside the image." - f" Expect X={x} to be to 0 to {w-1} and Y={y} to be to 0 to {h-1}." - ) - - if c == 1: - return Color.gray(orig_img[y, x]) - elif c == 3: - return Color.bgr(orig_img[y, x]) - elif c == 4: - return Color.bgra(orig_img[y, x]) - else: - raise ValueError(f"Unsupported number of channels: {c}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/flip.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/flip.py deleted file mode 100644 index b77f97c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/flip.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from api import KeyInfo -from nodes.impl.image_utils import FlipAxis -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import modification_group - - -@modification_group.register( - schema_id="chainner:image:flip", - name="Flip", - description="Flip an image.", - icon="MdFlip", - inputs=[ - ImageInput("Image"), - EnumInput(FlipAxis), - ], - outputs=[ImageOutput(shape_as=0, assume_normalized=True)], - key_info=KeyInfo.enum(1), -) -def flip_node(img: np.ndarray, axis: FlipAxis) -> np.ndarray: - return axis.flip(img) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/rotate.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/rotate.py deleted file mode 100644 index 20cea1c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/rotate.py +++ /dev/null @@ -1,134 +0,0 @@ -from __future__ import annotations - -import numpy as np - -from api import KeyInfo -from nodes.impl.pil_utils import ( - FillColor, - RotateSizeChange, - RotationInterpolationMethod, - rotate, -) -from nodes.properties.inputs import ( - EnumInput, - FillColorDropdown, - ImageInput, - RotateInterpolationInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput - -from .. import modification_group - - -@modification_group.register( - schema_id="chainner:image:rotate", - name="Rotate", - description="Rotate an image.", - icon="MdRotate90DegreesCcw", - inputs=[ - ImageInput("Image"), - SliderInput( - "Rotation Angle", - default=0, - maximum=360, - precision=1, - controls_step=45, - slider_step=1, - unit="°", - ), - RotateInterpolationInput(), - EnumInput( - RotateSizeChange, - label="Image Dimensions", - option_labels={ - RotateSizeChange.EXPAND: "Expand to fit", - RotateSizeChange.CROP: "Crop to original", - }, - ), - FillColorDropdown(), - ], - outputs=[ - ImageOutput( - image_type=""" - // This is a near verbatim copy of PIL's rotate code - // to get the size of the rotated image. - // https://pillow.readthedocs.io/en/stable/_modules/PIL/Image.html#Image.rotate - struct Point { x: number, y: number } - - let img = Input0; - let w = img.width; - let h = img.height; - let rot_center = Point { - x: w / 2, - y: h / 2, - }; - - let angleDeg = number::mod(Input1, 360); - let angle = -number::degToRad(angleDeg); - let m0 = number::cos(angle); - let m1 = number::sin(angle); - let m2 = rot_center.x + m0 * -rot_center.x + m1 * -rot_center.y; - let m3 = -number::sin(angle); - let m4 = number::cos(angle); - let m5 = rot_center.y + m3 * -rot_center.x + m4 * -rot_center.y; - - def transform(x: number, y: number) { - Point { - x: m0 * x + m1 * y + m2, - y: m3 * x + m4 * y + m5, - } - } - - let p0 = transform(0, 0); - let p1 = transform(w, 0); - let p2 = transform(w, h); - let p3 = transform(0, h); - - let expandWidth = Image.width & ( - ceil(max(p0.x, p1.x, p2.x, p3.x)) - - floor(min(p0.x, p1.x, p2.x, p3.x)) - ); - let expandHeight = Image.height & ( - ceil(max(p0.y, p1.y, p2.y, p3.y)) - - floor(min(p0.y, p1.y, p2.y, p3.y)) - ); - - struct Size { w: number, h: number } - let imgSize = Size { w, h }; - let transformedSize = match Input3 { - RotateSizeChange::Crop => imgSize, - RotateSizeChange::Expand => Size { w: expandWidth, h: expandHeight }, - }; - - // account for fast paths - let size = match angleDeg { - 0 | 180 | 360 => imgSize, - 90 | 270 => if Input3 == RotateSizeChange::Expand or w == h { - Size { w: h, h: w } - } else { - transformedSize - }, - _ => transformedSize, - }; - - Image { - width: size.w, - height: size.h, - channels: FillColor::getOutputChannels(Input4, img.channels) - } - """, - assume_normalized=True, - ) - ], - key_info=KeyInfo.number(1), - limited_to_8bpc=True, -) -def rotate_node( - img: np.ndarray, - angle: float, - interpolation: RotationInterpolationMethod, - expand: RotateSizeChange, - fill: FillColor, -) -> np.ndarray: - return rotate(img, angle, interpolation, expand, fill) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/shift.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/shift.py deleted file mode 100644 index 38c77bd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/image_utility/modification/shift.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -from nodes.groups import if_enum_group -from nodes.impl.image_utils import ShiftFill, shift -from nodes.properties.inputs import ( - EnumInput, - ImageInput, - NumberInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c, round_half_up - -from .. import modification_group - - -class ShiftCoordMode(Enum): - RELATIVE = 0 - ABSOLUTE = 1 - - -@modification_group.register( - schema_id="chainner:image:shift", - name="Shift", - description="Shift an image by an x, y amount.", - icon="BsGraphDown", - inputs=[ - ImageInput().with_id(0), - EnumInput( - ShiftCoordMode, default=ShiftCoordMode.ABSOLUTE, preferred_style="tabs" - ).with_id(4), - if_enum_group(4, ShiftCoordMode.RELATIVE)( - SliderInput("X", minimum=-100, maximum=100, default=0, unit="%").with_id(5), - SliderInput("Y", minimum=-100, maximum=100, default=0, unit="%").with_id(6), - ), - if_enum_group(4, ShiftCoordMode.ABSOLUTE)( - NumberInput("X", minimum=None, unit="px").with_id(1), - NumberInput("Y", minimum=None, unit="px").with_id(2), - ), - EnumInput( - ShiftFill, - label="Negative Space Fill", - default=ShiftFill.AUTO, - option_labels={ - ShiftFill.WRAP: "Wrap (Tile)", - }, - ).with_id(3), - ], - outputs=[ - ImageOutput( - image_type=""" - let i = Input0; - let fill = Input3; - match fill { - ShiftFill::Transparent => Image { width: i.width, height: i.height, channels: 4 }, - _ => i, - } - """, - assume_normalized=True, - ) - ], -) -def shift_node( - img: np.ndarray, - mode: ShiftCoordMode, - rel_x: int, - rel_y: int, - abs_x: int, - abs_y: int, - fill: ShiftFill, -) -> np.ndarray: - h, w, _ = get_h_w_c(img) - - if mode == ShiftCoordMode.RELATIVE: - abs_x = round_half_up(w * rel_x / 100) - abs_y = round_half_up(h * rel_y / 100) - - return shift(img, abs_x, abs_y, fill) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/__init__.py deleted file mode 100644 index 5e00833..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .. import material_textures_category - -normal_map_group = material_textures_category.add_node_group("Normal Map") -conversion_group = material_textures_category.add_node_group("Conversion") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/conversion/metal_to_specular.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/conversion/metal_to_specular.py deleted file mode 100644 index cb6d169..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/conversion/metal_to_specular.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import conversion_group - - -def get_size(img: np.ndarray) -> tuple[int, int]: - h, w, _ = get_h_w_c(img) - return w, h - - -def metal_to_spec( - albedo: np.ndarray, - metal: np.ndarray, - roughness: np.ndarray | None, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - assert get_h_w_c(albedo)[2] == 3, "Expected the albedo map to be an RGB image" - - # This uses the conversion method described here: - # https://marmoset.co/posts/pbr-texture-conversion/ - metal3 = np.dstack((metal,) * 3) - metal3_inv = 1 - metal3 - - albedo_size = get_size(albedo) - metal_size = get_size(metal) - - if metal_size == albedo_size: - metal3_inv_scaled = metal3_inv - else: - metal3_inv_scaled = resize(metal3_inv, albedo_size, ResizeFilter.LANCZOS) - diff = albedo * metal3_inv_scaled - - if metal_size == albedo_size: - scaled_albedo = albedo - else: - scaled_albedo = resize(albedo, metal_size, ResizeFilter.LANCZOS) - spec = metal3 * scaled_albedo + metal3_inv * 0.22 - - if roughness is None: - gloss = np.zeros((1, 1), np.float32) + 0.5 - else: - gloss = 1 - roughness - - return diff, spec, gloss - - -@conversion_group.register( - schema_id="chainner:image:metal_to_specular", - name="Metal to Specular", - description=("Converts a Metal/Roughness material into a Specular/Gloss material."), - icon="MdChangeCircle", - inputs=[ - ImageInput("Albedo", channels=[3, 4]), - ImageInput("Metal", channels=1), - ImageInput("Roughness", channels=1).make_optional(), - ], - outputs=[ - ImageOutput("Diffuse", shape_as=0), - ImageOutput( - "Specular", - image_type=navi.Image(size_as="Input1"), - channels=3, - ), - ImageOutput( - "Gloss", - image_type=""" - match Input2 { - Image as i => i, - null => Image { width: 1, height: 1, channels: 1 } - } - """, - channels=1, - ), - ], -) -def metal_to_specular_node( - albedo: np.ndarray, - metal: np.ndarray, - roughness: np.ndarray | None, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - albedo_channels = get_h_w_c(albedo)[2] - if albedo_channels == 4: - albedo_alpha = albedo[:, :, 3] - albedo = albedo[:, :, :3] - else: - albedo_alpha = None - - diff, spec, gloss = metal_to_spec(albedo, metal, roughness) - - if albedo_alpha is not None: - diff = np.dstack((diff, albedo_alpha)) - - return diff, spec, gloss diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/conversion/specular_to_metal.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/conversion/specular_to_metal.py deleted file mode 100644 index dc7afe9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/conversion/specular_to_metal.py +++ /dev/null @@ -1,134 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.resize import ResizeFilter, resize -from nodes.properties.inputs import ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import get_h_w_c - -from .. import conversion_group - - -def get_size(img: np.ndarray) -> tuple[int, int]: - h, w, _ = get_h_w_c(img) - return w, h - - -def spec_to_metal( - diff: np.ndarray, - spec: np.ndarray, - gloss: np.ndarray | None, - metallic_min: float, - metallic_max: float, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - assert get_h_w_c(diff)[2] == 3, "Expected the diffuse map to be an RGB image" - assert get_h_w_c(spec)[2] == 3, "Expected the specular map to be an RGB image" - - metallic_diff = ( - 0.0001 if metallic_min == metallic_max else metallic_max - metallic_min - ) - - # Metal is approximated using the magnitude of the specular map. - - spec_max = np.maximum(spec[:, :, 0], np.maximum(spec[:, :, 1], spec[:, :, 2])) - metal = np.clip((spec_max - metallic_min) / metallic_diff, 0, 1) - - # This uses the conversion method described here: - # https://marmoset.co/posts/pbr-texture-conversion/ - - diff_size = get_size(diff) - spec_size = get_size(spec) - - if diff_size == spec_size: - sped_scaled = spec - metal_scaled = metal - else: - # to prevent color bleeding from non-metal parts of the specular map, - # we apply the metal map as alpha and resize before combining with diffuse - scaled = resize(np.dstack((spec, metal)), diff_size, ResizeFilter.LANCZOS) - sped_scaled: np.ndarray = scaled[:, :, 0:3] - metal_scaled: np.ndarray = scaled[:, :, 3] - metal3_scaled = np.dstack((metal_scaled,) * 3) - albedo = metal3_scaled * sped_scaled + (1 - metal3_scaled) * diff - - if gloss is None: - roughness = np.zeros((1, 1), np.float32) + 0.5 - else: - roughness = 1 - gloss - - return albedo, metal, roughness - - -@conversion_group.register( - schema_id="chainner:image:specular_to_metal", - name="Specular to Metal", - description=("Converts a Specular/Gloss material into a Metal/Roughness material."), - icon="MdChangeCircle", - inputs=[ - ImageInput("Diffuse", channels=[3, 4]), - ImageInput("Specular", channels=3), - ImageInput("Gloss", channels=1).make_optional(), - SliderInput( - "Metallic Min", - minimum=0, - maximum=100, - default=23, - precision=1, - slider_step=1, - ), - SliderInput( - "Metallic Max", - minimum=0, - maximum=100, - default=30, - precision=1, - slider_step=1, - ), - ], - outputs=[ - ImageOutput("Albedo", shape_as=0), - ImageOutput( - "Metal", - image_type=navi.Image(size_as="Input1"), - channels=1, - ), - ImageOutput( - "Roughness", - image_type=""" - match Input2 { - Image as i => i, - null => Image { width: 1, height: 1, channels: 1 } - } - """, - channels=1, - ), - ], -) -def specular_to_metal_node( - diff: np.ndarray, - spec: np.ndarray, - gloss: np.ndarray | None, - metallic_min: float, - metallic_max: float, -) -> tuple[np.ndarray, np.ndarray, np.ndarray]: - metallic_min /= 100 - metallic_max /= 100 - - diff_channels = get_h_w_c(diff)[2] - - if diff_channels == 4: - diff_alpha = diff[:, :, 3] - diff = diff[:, :, :3] - else: - diff_alpha = None - - albedo, metal, roughness = spec_to_metal( - diff, spec, gloss, metallic_min, metallic_max - ) - - if diff_alpha is not None: - albedo = np.dstack((albedo, diff_alpha)) - - return albedo, metal, roughness diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/add_normals.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/add_normals.py deleted file mode 100644 index da98b6d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/add_normals.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.normals.addition import AdditionMethod, add_normals -from nodes.impl.normals.util import xyz_to_bgr -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import normal_map_group - - -@normal_map_group.register( - schema_id="chainner:image:add_normals", - name="Add Normals", - description="""Add 2 normal maps together. Only the R and G - channels of the input image will be used. The output normal map - is guaranteed to be normalized.""", - icon="MdAddCircleOutline", - inputs=[ - ImageInput("Normal Map 1", channels=[3, 4]), - SliderInput("Strength 1", maximum=200, default=100), - ImageInput("Normal Map 2", channels=[3, 4]), - SliderInput("Strength 2", maximum=200, default=100), - EnumInput( - AdditionMethod, - label="Method", - default=AdditionMethod.PARTIAL_DERIVATIVES, - ), - ], - outputs=[ - ImageOutput( - "Normal Map", - image_type=navi.Image( - width="Input0.width & Input2.width", - height="Input0.height & Input2.height", - ), - channels=3, - ).with_never_reason( - "The given normal maps have different sizes but must be the same size." - ), - ], -) -def add_normals_node( - n1: np.ndarray, - strength1: int, - n2: np.ndarray, - strength2: int, - method: AdditionMethod, -) -> np.ndarray: - return xyz_to_bgr( - add_normals( - method, - n1, - n2, - f1=strength1 / 100, - f2=strength2 / 100, - ) - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/convert_normals.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/convert_normals.py deleted file mode 100644 index 84b5cff..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/convert_normals.py +++ /dev/null @@ -1,94 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.image_utils import NormalMapType -from nodes.impl.normals.util import ( - XYZ, - gr_to_xyz, - octahedral_gr_to_xyz, - xyz_to_bgr, - xyz_to_octahedral_bgr, -) -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import normal_map_group - - -@normal_map_group.register( - schema_id="chainner:image:convert_normal_map", - name="Convert Normals", - description=[ - """Convert between different normal map formats. Only the R and G - channels of the input image will be used. For DirectX and OpenGL, the output normal map - is guaranteed to be normalized.""", - "Also supports converting to and from the Octahedral format used by RTX Remix.", - ], - icon="BsBoxArrowUpRight", - inputs=[ - ImageInput("Normal Map", channels=[3, 4]), - EnumInput( - NormalMapType, - label="From", - label_style="inline", - default=NormalMapType.DIRECTX, - option_labels={ - NormalMapType.DIRECTX: "DirectX", - NormalMapType.OPENGL: "OpenGL", - }, - ), - EnumInput( - NormalMapType, - label="To", - label_style="inline", - default=NormalMapType.OPENGL, - option_labels={ - NormalMapType.DIRECTX: "DirectX", - NormalMapType.OPENGL: "OpenGL", - NormalMapType.OCTAHEDRAL: "Octahedral (RTX Remix)", - }, - ), - ], - outputs=[ - ImageOutput( - "Normal Map", - image_type=navi.Image( - width="Input0.width", - height="Input0.height", - ), - channels=3, - ), - ], -) -def convert_normals_node( - img: np.ndarray, - from_type: NormalMapType, - to_type: NormalMapType, -) -> np.ndarray: - # Step 1: Read/decode the image to get the XYZ components of the normals - - # we define this as DirectX normals - xyz: XYZ - if from_type == NormalMapType.DIRECTX: - xyz = gr_to_xyz(img) - elif from_type == NormalMapType.OPENGL: - xyz = gr_to_xyz(img) - # OpenGL to DirectX - _, y, _ = xyz - np.negative(y, out=y) - elif from_type == NormalMapType.OCTAHEDRAL: - xyz = octahedral_gr_to_xyz(img) - - # Step 2: Convert/encode the XYZ components of the normals to BGR - - if to_type == NormalMapType.DIRECTX: - return xyz_to_bgr(xyz) - elif to_type == NormalMapType.OPENGL: - # DirectX to OpenGL - _, y, _ = xyz - np.negative(y, out=y) - return xyz_to_bgr(xyz) - elif to_type == NormalMapType.OCTAHEDRAL: - return xyz_to_octahedral_bgr(xyz) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normal_map_generator.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normal_map_generator.py deleted file mode 100644 index d5e7dcb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normal_map_generator.py +++ /dev/null @@ -1,343 +0,0 @@ -from __future__ import annotations - -import math -from enum import Enum - -import cv2 -import numpy as np - -import navi -from nodes.groups import icon_set_group, if_enum_group -from nodes.impl.image_utils import BorderType, create_border, fast_gaussian_blur -from nodes.impl.normals.edge_filter import EdgeFilter, get_filter_kernels -from nodes.impl.normals.height import HeightSource, get_height_map -from nodes.properties.inputs import ( - BoolInput, - EnumInput, - ImageInput, - SliderInput, -) -from nodes.properties.outputs import ImageOutput -from nodes.utils.utils import Padding, get_h_w_c - -from .. import normal_map_group - - -class AlphaOutput(Enum): - NONE = "none" - UNCHANGED = "unchanged" - HEIGHT = "height" - ONE = "one" - - -def as_grayscale(img: np.ndarray) -> np.ndarray: - c = get_h_w_c(img)[2] - if c == 1: - return img - if c == 3: - return cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - raise AssertionError("Only grayscale and RGB images are supported.") - - -def normalize(x: np.ndarray, y: np.ndarray): - h, w, _ = get_h_w_c(x) - # No idea why, but that's the value NvTT uses - z = 2 - l = np.sqrt(np.square(x) + np.square(y) + z * z) - return x / l, y / l, np.ones((h, w), dtype=np.float32) * z / l - - -@normal_map_group.register( - schema_id="chainner:image:normal_generator", - name="Normal Map Generator", - description=[ - "Generate a normal map from a given image using the specified filtering technique.", - "The node will first convert the given image into a height map. A filter is then applied to the height map to calculate the normal map.", - "### Height map generation", - "Since this node needs a height map, it will always convert the input image into one. The **Height** input determines how this conversion happens.", - "Generally, if you have already have a good height map for a texture, use it with *Average RGB* for best results.", - "If you have a albedo/diffuse texture, most height sources will approximate the height map using pixel brightness. This is a very crude approximation, but can work well enough. Start with *Average RGB* and test our difference filters before using a different height source.", - "### Filters", - "There are many filters available. Generally, the *Sobel (dUdV) (3x3)* filter is a good choice for obtaining a normal map. Since the filter is small and not too aggressive, it works well even with crude height maps (e.g. height maps obtained from albedo/diffuse textures). For a more aggressive filter, try *Scharr (3x3)* or *4 Sample (1x3)*.", - "If you want more control over the output normal map, use the *Multi Gaussian* filter. This filter operates on multiple frequencies and allows you to control the strength of each frequency. This allows you to adjust the influence of tiny details and large features. *Scale 1** is the strength of the smallest details, and **Scale 8** is the strength of the largest features.", - "Note: If you set **Scale 1** to 1 and all other scales to 0, you will get (almost) the same result as the *4 Sample (1x3)* filter. If you set **Scale 2** to 1 and all other scales to 0, you will get (almost) the same result as the *Sobel (dUdV) (3x3)* filter.", - ], - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput("Image", channels=[1, 3, 4]), - BoolInput("Tileable", default=False) - .with_docs( - "If enabled, the input texture will be treated as tileable and a tileable normal map will be created.", - hint=True, - ) - .with_id(16), - EnumInput( - HeightSource, - label="Height", - label_style="inline", - default=HeightSource.AVERAGE_RGB, - ) - .with_docs( - "Given the R, G, B, A channels of the input image, a height map will be calculated as follows:", - "- Average RGB: `Height = (R + G + B) / 3`", - "- Max RGB: `Height = max(R, G, B)`", - "- Screen RGB: `Height = 1 - ((1 - R) * (1 - G) * (1 - B))`", - "- Red: `Height = R`", - "- Green: `Height = G`", - "- Blue: `Height = B`", - "- Alpha: `Height = A`", - ) - .with_id(1), - SliderInput( - "Blur/Sharp", - minimum=-20, - maximum=20, - default=0, - precision=1, - ) - .with_docs( - "A quick way to blur or sharpen the height map. Negative values blur, positive values sharpen." - ) - .with_id(2), - SliderInput( - "Min Z", - minimum=0, - maximum=1, - default=0, - precision=3, - slider_step=0.01, - controls_step=0.05, - ) - .with_docs( - "A minimum height that can be used to cut off low height values.", - "This value is generally only useful in specific circumstances, so it's usually best to leave it at 0.", - ) - .with_id(3), - SliderInput( - "Scale", - minimum=0, - maximum=100, - default=1, - precision=3, - controls_step=0.1, - scale="log-offset", - ) - .with_docs( - "A factor applied to the height map.", - "The smaller the scale, the most flat the output normal map will be. The large the scale, the more pronounced the normal map will be.", - ) - .with_id(4), - EnumInput( - EdgeFilter, - label="Filter", - label_style="inline", - default=EdgeFilter.SOBEL, - option_labels={ - EdgeFilter.SOBEL: "Sobel (dUdV) (3x3)", - EdgeFilter.SOBEL_LIKE_5: "Sobel-like (5x5)", - EdgeFilter.SOBEL_LIKE_7: "Sobel-like (7x7)", - EdgeFilter.SOBEL_LIKE_9: "Sobel-like (9x9)", - EdgeFilter.PREWITT: "Prewitt (3x3)", - EdgeFilter.SCHARR: "Scharr (3x3)", - EdgeFilter.FOUR_SAMPLE: "4 Sample (1x3)", - EdgeFilter.MULTI_GAUSS: "Multi Gaussian", - }, - ).with_id(5), - if_enum_group(5, EdgeFilter.MULTI_GAUSS)( - SliderInput( - "Scale 1", - minimum=0, - maximum=10, - default=0.25, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(8), - SliderInput( - "Scale 2", - minimum=0, - maximum=10, - default=0.5, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(9), - SliderInput( - "Scale 3", - minimum=0, - maximum=10, - default=0.3, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(10), - SliderInput( - "Scale 4", - minimum=0, - maximum=10, - default=0.25, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(11), - SliderInput( - "Scale 5", - minimum=0, - maximum=10, - default=0.2, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(12), - SliderInput( - "Scale 6", - minimum=0, - maximum=10, - default=0.15, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(13), - SliderInput( - "Scale 7", - minimum=0, - maximum=10, - default=0.10, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(14), - SliderInput( - "Scale 8", - minimum=0, - maximum=10, - default=0.10, - precision=3, - controls_step=0.1, - scale="log-offset", - has_handle=False, - ).with_id(15), - ), - icon_set_group("Invert")( - BoolInput("Invert R", default=False, icon="R") - .with_docs("Whether to invert the R/X channels of the normal map.") - .with_id(17), - BoolInput("Invert G", default=False, icon="G") - .with_docs("Whether to invert the G/Y channels of the normal map.") - .with_id(18), - ), - EnumInput( - AlphaOutput, - label="Alpha", - label_style="inline", - default=AlphaOutput.NONE, - option_labels={AlphaOutput.ONE: "Set to 1"}, - ) - .with_docs("Determines the alpha channel of the generated normal map.") - .with_id(7), - ], - outputs=[ - ImageOutput( - "Normal Map", - image_type=navi.Image( - size_as="Input0", - channels="match Input7 { AlphaOutput::None => 3, _ => 4 }", - ), - ), - ], -) -def normal_map_generator_node( - img: np.ndarray, - tileable: bool, - height_source: HeightSource, - blur_sharp: float, - min_z: float, - scale: float, - edge_filter: EdgeFilter, - gauss_scale1: float, - gauss_scale2: float, - gauss_scale3: float, - gauss_scale4: float, - gauss_scale5: float, - gauss_scale6: float, - gauss_scale7: float, - gauss_scale8: float, - invert_r: bool, - invert_g: bool, - alpha_output: AlphaOutput, -) -> np.ndarray: - h, w, c = get_h_w_c(img) - height = get_height_map(img, height_source) - - filter_x, filter_y = get_filter_kernels( - edge_filter, - gauss_parameter=[ - (1 / 4, gauss_scale1), - (2 / 4, gauss_scale2), - (4 / 4, gauss_scale3), - (8 / 4, gauss_scale4), - (16 / 4, gauss_scale5), - (32 / 4, gauss_scale6), - (64 / 4, gauss_scale7), - (128 / 4, gauss_scale8), - ], - ) - - padding = 0 - if tileable: - padding = max(1, filter_x.shape[0] // 2, math.ceil(abs(blur_sharp) * 2)) - height = create_border(height, BorderType.WRAP, Padding.all(padding)) - - if blur_sharp < 0: - # blur - height = fast_gaussian_blur(height, -blur_sharp) - elif blur_sharp > 0: - # sharpen - blurred = fast_gaussian_blur(height, blur_sharp) - height = cv2.addWeighted(height, 2.0, blurred, -1.0, 0) - - if min_z > 0: - height = np.maximum(min_z, height) - if scale != 0: - height = height * scale # type: ignore - - dx = cv2.filter2D(height, -1, filter_x) - dy = cv2.filter2D(height, -1, filter_y) - - if padding > 0: - dx = dx[padding:-padding, padding:-padding] - dy = dy[padding:-padding, padding:-padding] - height = height[padding:-padding, padding:-padding] - - x, y, z = normalize(dx, dy) - - if invert_r: - x = -x - if invert_g: - y = -y - - if alpha_output is AlphaOutput.NONE: - a = None - elif alpha_output is AlphaOutput.HEIGHT: - a = height - elif alpha_output is AlphaOutput.UNCHANGED: - a = np.ones((h, w), dtype=np.float32) if c < 4 else img[:, :, 3] - elif alpha_output is AlphaOutput.ONE: - a = np.ones((h, w), dtype=np.float32) - else: - raise AssertionError(f"Invalid alpha output '{alpha_output}'") - - r = (x + 1) * 0.5 - g = (y + 1) * 0.5 - b = np.abs(z) - - channels = (b, g, r) if a is None else (b, g, r, a) - - return cv2.merge(channels) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normalize_normal_map.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normalize_normal_map.py deleted file mode 100644 index 0fc05ce..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normalize_normal_map.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.normals.util import gr_to_xyz, xyz_to_bgr -from nodes.properties.inputs import ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import normal_map_group - - -@normal_map_group.register( - schema_id="chainner:image:normalize_normal_map", - name="Normalize Normal Map", - description="""Normalizes the given normal map. - Only the R and G channels of the input image will be used to compute the unit vectors.""", - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput("Normal Map", channels=[3, 4]), - ], - outputs=[ - ImageOutput( - "Normal Map", - image_type=navi.Image(size_as="Input0"), - channels=3, - ), - ], -) -def normalize_normal_map_node(img: np.ndarray) -> np.ndarray: - """Takes a normal map and normalizes it""" - - return xyz_to_bgr(gr_to_xyz(img)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normalize_normals.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normalize_normals.py deleted file mode 100644 index d23441b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/normalize_normals.py +++ /dev/null @@ -1,66 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -import numpy as np - -import navi -from nodes.impl.normals.util import gr_to_xyz, xyz_to_bgr -from nodes.properties.inputs import EnumInput, ImageInput -from nodes.properties.outputs import ImageOutput - -from .. import normal_map_group - - -class BChannel(Enum): - Z = 0 - Z_MAPPED = 1 - ZERO = 2 - HALF = 3 - ONE = 4 - - -@normal_map_group.register( - schema_id="chainner:image:normalize_normal_map", - name="Normalize Normals", - description=[ - "Normalizes the given normal map. Only the R and G channels of the input image will be used to compute the unit vectors.", - "While the X and Y component will always be mapped from [-1, 1] to [0, 1] and saved as the R and G channels respectively, the B channel can be configured to contain different values.", - ], - icon="MdOutlineAutoFixHigh", - inputs=[ - ImageInput("Normal Map", channels=[3, 4]), - EnumInput( - BChannel, "Output B", label_style="inline", default=BChannel.Z - ).with_docs( - "Determines the content of the B channel of the output normal map.", - "- `Z`: Unlike the X and Y components which are in range [-1, 1], the Z component is guaranteed to be in the range [0, 1]. This allows us to directly use the Z component as the B channel.", - "- `Z Mapped`: Just like the X and Y components, the Z component will be mapped to [0, 1] and stored as the B channel. Since the Z component is always >= 0, the B channel will be in the range [0.5, 1] ([128, 255])", - "- `ONE`: The B channel will be 1 (255) everywhere.", - "- `HALF`: The B channel will be 0.5 (128) everywhere.", - "- `ZERO`: The B channel will be 0 everywhere.", - ), - ], - outputs=[ - ImageOutput( - "Normal Map", - image_type=navi.Image(size_as="Input0"), - channels=3, - ), - ], -) -def normalize_normals_node(img: np.ndarray, b: BChannel) -> np.ndarray: - result = xyz_to_bgr(gr_to_xyz(img)) - - if b == BChannel.Z_MAPPED: - result[:, :, 0] = (result[:, :, 0] + 1) / 2 - elif b == BChannel.ZERO: - result[:, :, 0] = 0 - elif b == BChannel.HALF: - result[:, :, 0] = 0.5 - elif b == BChannel.ONE: - result[:, :, 0] = 1 - elif b == BChannel.Z: - pass - - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/scale_normals.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/scale_normals.py deleted file mode 100644 index c3a2a90..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/material_textures/normal_map/scale_normals.py +++ /dev/null @@ -1,42 +0,0 @@ -from __future__ import annotations - -import numpy as np - -import navi -from nodes.impl.normals.addition import AdditionMethod, strengthen_normals -from nodes.impl.normals.util import xyz_to_bgr -from nodes.properties.inputs import EnumInput, ImageInput, SliderInput -from nodes.properties.outputs import ImageOutput - -from .. import normal_map_group - - -@normal_map_group.register( - schema_id="chainner:image:strengthen_normals", - name="Scale Normals", - description=[ - "Strengths or weakens the normals in the given normal map. Only the R and G channels of the input image will be used. The output normal map is guaranteed to be normalized.", - "Conceptually, this node is equivalent to `chainner:image:add_normals` with the strength of the second normal map set to 0.", - ], - icon="MdExpand", - inputs=[ - ImageInput("Normal Map", channels=[3, 4]), - SliderInput("Strength", maximum=400, default=100), - EnumInput( - AdditionMethod, - label="Method", - default=AdditionMethod.PARTIAL_DERIVATIVES, - ), - ], - outputs=[ - ImageOutput( - "Normal Map", - image_type=navi.Image(size_as="Input0"), - channels=3, - ), - ], -) -def scale_normals_node( - n: np.ndarray, strength: int, method: AdditionMethod -) -> np.ndarray: - return xyz_to_bgr(strengthen_normals(method, n, strength / 100)) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/__init__.py deleted file mode 100644 index 3194c4c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from .. import utility_category - -clipboard_group = utility_category.add_node_group("Clipboard") -value_group = utility_category.add_node_group("Value") -math_group = utility_category.add_node_group("Math") -text_group = utility_category.add_node_group("Text") -color_group = utility_category.add_node_group("Color") -random_group = utility_category.add_node_group("Random") -directory_group = utility_category.add_node_group("Directory") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/clipboard/copy_to_clipboard.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/clipboard/copy_to_clipboard.py deleted file mode 100644 index c4d103d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/clipboard/copy_to_clipboard.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -import numpy as np -from chainner_ext import Clipboard - -from nodes.properties.inputs import ClipboardInput - -from .. import clipboard_group - - -@clipboard_group.register( - schema_id="chainner:utility:copy_to_clipboard", - name="Copy To Clipboard", - description="Copies the input to the clipboard.", - icon="BsClipboard", - inputs=[ - ClipboardInput(), - ], - outputs=[], - side_effects=True, - limited_to_8bpc="The image will be copied to clipboard with 8 bits/channel.", -) -def copy_to_clipboard_node(value: str | np.ndarray) -> None: - if isinstance(value, np.ndarray): - Clipboard.create_instance().write_image(value, "BGR") - else: - Clipboard.create_instance().write_text(value) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/color/color.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/color/color.py deleted file mode 100644 index 95df929..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/color/color.py +++ /dev/null @@ -1,35 +0,0 @@ -from __future__ import annotations - -from api import KeyInfo -from nodes.impl.color.color import Color -from nodes.properties.inputs import ColorInput -from nodes.properties.outputs import ColorOutput - -from .. import color_group - - -@color_group.register( - schema_id="chainner:utility:color", - name="Color", - description="Outputs the given color.", - icon="MdColorLens", - inputs=[ - ColorInput().make_fused(), - ], - outputs=[ - ColorOutput(color_type="Input0").suggest(), - ], - key_info=KeyInfo.type( - """ - let channels = Input0.channels; - match channels { - 1 => "Gray", - 3 => "RGB", - 4 => "RGBA", - _ => never - } - """ - ), -) -def color_node(color: Color) -> Color: - return color diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/color/color_from.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/color/color_from.py deleted file mode 100644 index 3182b55..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/color/color_from.py +++ /dev/null @@ -1,119 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from api import KeyInfo -from nodes.groups import if_enum_group -from nodes.impl.color.color import Color -from nodes.properties.inputs import EnumInput, SliderInput -from nodes.properties.outputs import ColorOutput - -from .. import color_group - - -class ColorType(Enum): - GRAY = 0 - RGB = 1 - RGBA = 2 - - -@color_group.register( - schema_id="chainner:utility:color_from_channels", - name="Color From", - description="Create a new color value from individual channels.", - icon="MdColorLens", - inputs=[ - EnumInput( - ColorType, "Color Type", ColorType.RGBA, preferred_style="tabs" - ).with_id(0), - if_enum_group(0, ColorType.GRAY)( - SliderInput( - "Luma", - minimum=0, - maximum=255, - default=128, - precision=1, - slider_step=1, - controls_step=1, - hide_trailing_zeros=True, - gradient=["#000000", "#ffffff"], - ), - ), - if_enum_group(0, (ColorType.RGB, ColorType.RGBA))( - SliderInput( - "Red", - minimum=0, - maximum=255, - default=128, - precision=1, - slider_step=1, - controls_step=1, - hide_trailing_zeros=True, - gradient=["#000000", "#ff0000"], - ), - SliderInput( - "Green", - minimum=0, - maximum=255, - default=128, - precision=1, - slider_step=1, - controls_step=1, - hide_trailing_zeros=True, - gradient=["#000000", "#00ff00"], - ), - SliderInput( - "Blue", - minimum=0, - maximum=255, - default=128, - precision=1, - slider_step=1, - controls_step=1, - hide_trailing_zeros=True, - gradient=["#000000", "#0000ff"], - ), - ), - if_enum_group(0, ColorType.RGBA)( - SliderInput( - "Alpha", - minimum=0, - maximum=100, - default=100, - precision=1, - slider_step=1, - controls_step=1, - unit="%", - ), - ), - ], - outputs=[ - ColorOutput( - color_type=""" - let channels = match Input0 { - ColorType::Gray => 1, - ColorType::Rgb => 3, - ColorType::Rgba => 4, - }; - Color { channels } - """ - ) - ], - key_info=KeyInfo.enum(0), -) -def color_from_node( - color_type: ColorType, - gray: float, - red: float, - green: float, - blue: float, - alpha: float, -) -> Color: - if color_type == ColorType.GRAY: - return Color.gray(gray / 255) - if color_type == ColorType.RGB: - return Color.bgr([blue / 255, green / 255, red / 255]) - if color_type == ColorType.RGBA: - return Color.bgra([blue / 255, green / 255, red / 255, alpha / 100]) - else: - raise AssertionError(f"Invalid color type {color_type}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_go_into.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_go_into.py deleted file mode 100644 index 00e3770..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_go_into.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from nodes.groups import optional_list_group -from nodes.properties.inputs import DirectoryInput, RelativePathInput -from nodes.properties.outputs import DirectoryOutput - -from .. import directory_group - - -@directory_group.register( - schema_id="chainner:utility:into_directory", - name="Directory Go Into", - description="Goes forward into a directory.", - icon="BsFolder", - inputs=[ - DirectoryInput(must_exist=False, label_style="hidden"), - RelativePathInput("Folder"), - optional_list_group( - *[RelativePathInput(f"Folder {i}").make_optional() for i in range(2, 11)], - ), - ], - outputs=[ - DirectoryOutput( - output_type=""" - def into(dir: Directory | Error, folder: string | null): Directory | Error { - match dir { - Error as e => e, - Directory => { - match folder { - null => dir, - string => { - let result = goIntoDirectory(dir.path, folder); - match result { - string => Directory { path: result }, - Error => result, - } - }, - } - }, - } - } - - let d1 = into(Input0, Input1); - let d2 = into(d1, Input2); - let d3 = into(d2, Input3); - let d4 = into(d3, Input4); - let d5 = into(d4, Input5); - let d6 = into(d5, Input6); - let d7 = into(d6, Input7); - let d8 = into(d7, Input8); - let d9 = into(d8, Input9); - let d10 = into(d9, Input10); - d10 - """, - ), - ], -) -def directory_go_into_node(directory: Path, *folders: str | None) -> Path: - for folder in folders: - if folder is not None: - directory = (directory / folder).resolve() - return directory diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_go_up.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_go_up.py deleted file mode 100644 index 561491d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_go_up.py +++ /dev/null @@ -1,37 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from nodes.properties.inputs import DirectoryInput, NumberInput -from nodes.properties.outputs import DirectoryOutput - -from .. import directory_group - - -@directory_group.register( - schema_id="chainner:utility:back_directory", - name="Directory Go Up", - description="Traverse up from a directory the specified number of times.", - icon="BsFolder", - inputs=[ - DirectoryInput(must_exist=False, label_style="hidden"), - NumberInput("Times", minimum=0, default=1, label_style="inline").with_docs( - "How many times to go up.", - "- 0 will return the given directory as is.", - "- 1 will return the parent directory.", - "- 2 will return the grandparent directory.", - "- etc.", - hint=True, - ), - ], - outputs=[ - DirectoryOutput( - output_type="Directory { path: getParentDirectory(Input0.path, Input1) }", - ), - ], -) -def directory_go_up_node(directory: Path, amt: int) -> Path: - result = directory - for _ in range(amt): - result = result.parent - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_to_text.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_to_text.py deleted file mode 100644 index 2c5b074..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/directory/directory_to_text.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from nodes.properties.inputs import DirectoryInput -from nodes.properties.outputs import TextOutput - -from .. import directory_group - - -@directory_group.register( - schema_id="chainner:utility:directory_to_text", - name="Directory to Text", - description="Converts a directory path into text.", - icon="BsFolder", - inputs=[ - DirectoryInput(must_exist=False, label_style="hidden"), - ], - outputs=[ - TextOutput( - "Dir Text", - output_type="Input0.path", - ), - ], -) -def directory_to_text_node(directory: Path) -> str: - return str(directory) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/accumulate.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/accumulate.py deleted file mode 100644 index f174965..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/accumulate.py +++ /dev/null @@ -1,103 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from api import BaseInput, Collector, IteratorInputInfo -from nodes.properties.inputs import EnumInput -from nodes.properties.outputs import NumberOutput - -from .. import math_group - - -class AnyNumberInput(BaseInput): - def __init__(self, label: str): - super().__init__( - input_type="number", - label=label, - kind="generic", - has_handle=True, - associated_type=float, - ) - - -class Operation(Enum): - SUM = "sum" - PRODUCT = "prod" - MAXIMUM = "max" - MINIMUM = "min" - - @property - def neutral(self) -> float: - if self == Operation.SUM: - return 0 - elif self == Operation.PRODUCT: - return 1 - elif self == Operation.MAXIMUM: - return float("-inf") - elif self == Operation.MINIMUM: - return float("inf") - else: - raise NotImplementedError() - - def reduce(self, a: float, b: float) -> float: - if self == Operation.SUM: - return a + b - elif self == Operation.PRODUCT: - return a * b - elif self == Operation.MAXIMUM: - return max(a, b) - elif self == Operation.MINIMUM: - return min(a, b) - else: - raise NotImplementedError() - - -@math_group.register( - schema_id="chainner:utility:accumulate", - name="Accumulate", - description="Calculates a single number of a sequence of number.", - icon="MdCalculate", - inputs=[ - AnyNumberInput("Numbers"), - EnumInput(Operation), - ], - iterator_inputs=IteratorInputInfo(inputs=0), - outputs=[ - NumberOutput( - "Result", - output_type=""" - let x = Input0; - let op = Input1; - let length = int(0..); // TODO: iterator sequence length - - let neutral = match op { - Operation::Sum => 0, - Operation::Product => 1, - Operation::Maximum => -inf, - Operation::Minimum => inf, - }; - - match length { - 0 => neutral, - uint => match op { - Operation::Sum => x * length, - Operation::Product => x ** length, - Operation::Maximum => x, - Operation::Minimum => x, - } - } - """, - ) - ], - kind="collector", -) -def accumulate_node(_: None, operation: Operation) -> Collector[float, float]: - result = [operation.neutral] - - def on_iterate(x: float): - result[0] = operation.reduce(result[0], x) - - def on_complete(): - return result[0] - - return Collector(on_iterate=on_iterate, on_complete=on_complete) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/math.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/math.py deleted file mode 100644 index 1f3015a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/math.py +++ /dev/null @@ -1,131 +0,0 @@ -from __future__ import annotations - -import math -from enum import Enum - -from nodes.properties.inputs import EnumInput, NumberInput -from nodes.properties.outputs import NumberOutput - -from .. import math_group - - -class MathOperation(Enum): - ADD = "add" - SUBTRACT = "sub" - MULTIPLY = "mul" - DIVIDE = "div" - POWER = "pow" - LOG = "log" - MAXIMUM = "max" - MINIMUM = "min" - MODULO = "mod" - PERCENT = "percent" - - -OP_LABEL: dict[MathOperation, str] = { - MathOperation.ADD: "Add: a + b", - MathOperation.SUBTRACT: "Subtract: a - b", - MathOperation.MULTIPLY: "Multiply: a × b", # noqa: RUF001 - MathOperation.DIVIDE: "Divide: a ÷ b", - MathOperation.POWER: "Exponent: a ^ b", - MathOperation.LOG: "Logarithm: log a of b", - MathOperation.MAXIMUM: "Maximum: max(a, b)", - MathOperation.MINIMUM: "Minimum: min(a, b)", - MathOperation.MODULO: "Modulo: a mod b", - MathOperation.PERCENT: "Percent: a × b ÷ 100", # noqa: RUF001 -} - -_special_mod_numbers = (0.0, float("inf"), float("-inf"), float("nan")) - - -@math_group.register( - schema_id="chainner:utility:math", - name="Math", - description="Perform mathematical operations on numbers.", - see_also=[ - "chainner:utility:math_round", - "chainner:utility:number", - ], - icon="MdCalculate", - inputs=[ - NumberInput( - "Operand a", - minimum=None, - maximum=None, - precision=100, - controls_step=1, - ), - EnumInput(MathOperation, "Math Operation", option_labels=OP_LABEL), - NumberInput( - "Operand b", - minimum=None, - maximum=None, - precision=100, - controls_step=1, - ), - ], - outputs=[ - NumberOutput( - "Result", - output_type=""" - let a = Input0; - let b = Input2; - - def nonZero(x: number): number { - match x { - 0 => never, - _ as x => x, - } - } - - match Input1 { - MathOperation::Add => a + b, - MathOperation::Subtract => a - b, - MathOperation::Multiply => a * b, - MathOperation::Divide => a / nonZero(b), - MathOperation::Power => a ** b, - MathOperation::Log => number::log(a) / number::log(b), - MathOperation::Maximum => max(a, b), - MathOperation::Minimum => min(a, b), - MathOperation::Modulo => number::mod(a, b), - MathOperation::Percent => a * b / 100, - } - """, - ).with_never_reason( - "The mathematical operation is not defined. This is most likely a divide by zero error." - ) - ], -) -def math_node(a: float, op: MathOperation, b: float) -> int | float: - if op == MathOperation.ADD: - return a + b - elif op == MathOperation.SUBTRACT: - return a - b - elif op == MathOperation.MULTIPLY: - return a * b - elif op == MathOperation.DIVIDE: - return a / b - elif op == MathOperation.POWER: - try: - result = pow(a, b) - except Exception as e: - raise ValueError(f"{a}^{b} is not defined for real numbers.") from e - - if isinstance(result, (int, float)): - return result - raise ValueError(f"{a}^{b} is not defined for real numbers.") - elif op == MathOperation.LOG: - return math.log(b, a) - elif op == MathOperation.MAXIMUM: - return max(a, b) - elif op == MathOperation.MINIMUM: - return min(a, b) - elif op == MathOperation.MODULO: - if a in _special_mod_numbers or b in _special_mod_numbers: - return a - b * math.floor(a / b) - else: - return a % b - elif op == MathOperation.PERCENT: - return a * b / 100 - else: - raise RuntimeError(f"Unknown operator {op}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/round.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/round.py deleted file mode 100644 index 2b803ff..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/math/round.py +++ /dev/null @@ -1,124 +0,0 @@ -from __future__ import annotations - -import math -from enum import Enum - -import numpy as np - -from nodes.groups import if_enum_group -from nodes.properties.inputs import EnumInput, NumberInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.utils import round_half_up - -from .. import math_group - - -class RoundOperation(Enum): - FLOOR = "Round down" - CEILING = "Round up" - ROUND = "Round" - - -class RoundScale(Enum): - UNIT = "Integer" - MULTIPLE = "Multiple of..." - POWER = "Power of..." - - -@math_group.register( - schema_id="chainner:utility:math_round", - name="Round", - description="Round an input number", - icon="MdCalculate", - inputs=[ - NumberInput( - "Input", - minimum=None, - maximum=None, - precision=100, - controls_step=1, - ), - EnumInput( - RoundOperation, - "Operation", - option_labels={k: k.value for k in RoundOperation}, - ), - EnumInput( - RoundScale, - "To the nearest", - option_labels={k: k.value for k in RoundScale}, - ), - if_enum_group(2, RoundScale.MULTIPLE)( - NumberInput( - "Multiple", - default=1, - minimum=1e-100, - maximum=None, - precision=100, - controls_step=1, - ) - ), - if_enum_group(2, RoundScale.POWER)( - NumberInput( - "Power", - default=2, - minimum=np.nextafter(1.0, np.inf), - maximum=None, - precision=100, - controls_step=1, - ) - ), - ], - outputs=[ - NumberOutput( - "Result", - output_type=""" - let x = Input0; - let m = Input3; - let p = Input4; - - match Input2 { - RoundScale::Unit => match Input1 { - RoundOperation::Floor => floor(x), - RoundOperation::Ceiling => ceil(x), - RoundOperation::Round => round(x), - }, - RoundScale::Multiple => match Input1 { - RoundOperation::Floor => floor(x/m) * m, - RoundOperation::Ceiling => ceil(x/m) * m, - RoundOperation::Round => round(x/m) * m, - }, - RoundScale::Power => match Input1 { - RoundOperation::Floor => p ** floor(number::log(x)/number::log(p)), - RoundOperation::Ceiling => p ** ceil(number::log(x)/number::log(p)), - RoundOperation::Round => p ** round(number::log(x)/number::log(p)), - }, - } - """, - ) - ], -) -def round_node( - a: float, - operation: RoundOperation, - scale: RoundScale, - m: float, - p: float, -) -> int | float: - if operation == RoundOperation.FLOOR: - op = math.floor - elif operation == RoundOperation.CEILING: - op = math.ceil - elif operation == RoundOperation.ROUND: - op = round_half_up - else: - raise RuntimeError(f"Unknown operation {operation}") - - if scale == RoundScale.UNIT: - return op(a) - elif scale == RoundScale.MULTIPLE: - return op(a / m) * m - elif scale == RoundScale.POWER: - return p ** op(math.log(a, p)) - else: - raise RuntimeError(f"Unknown scale {scale}") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/random/derive_seed.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/random/derive_seed.py deleted file mode 100644 index 0a48614..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/random/derive_seed.py +++ /dev/null @@ -1,68 +0,0 @@ -from __future__ import annotations - -import hashlib -import struct -from typing import Union - -from nodes.groups import optional_list_group, seed_group -from nodes.properties.inputs import BaseInput, SeedInput -from nodes.properties.outputs import SeedOutput -from nodes.utils.seed import Seed -from nodes.utils.utils import ALPHABET - -from .. import random_group - -Source = Union[int, float, str, Seed] - - -def SourceInput(label: str): - return BaseInput( - kind="generic", - label=label, - input_type="number | string | Directory | Seed", - ).make_optional() - - -def _to_bytes(s: Source) -> bytes: - if isinstance(s, str): - return s.encode(errors="backslashreplace") - if isinstance(s, Seed): - s = s.value - - i = int(s) - if isinstance(s, int) or s == i: # type: ignore - return i.to_bytes(i.bit_length() // 8 + 1, byteorder="big", signed=True) - - return struct.pack("d", s) - - -@random_group.register( - schema_id="chainner:utility:derive_seed", - name="Derive Seed", - description="Creates a new seed from multiple sources of randomness.", - icon="MdCalculate", - inputs=[ - seed_group(SeedInput(has_handle=False)), - SourceInput("Source A"), - optional_list_group( - *[SourceInput(f"Source {letter}") for letter in ALPHABET[1:10]], - ), - ], - outputs=[ - SeedOutput(), - ], -) -def derive_seed_node(seed: Seed, *sources: Source | None) -> Seed: - if all(s is None for s in sources): - # return seed as is if there are no sources of randomness - # this is useful for extracting out seeds - return seed - - h = hashlib.sha256() - - h.update(_to_bytes(seed)) - for s in sources: - if s is not None: - h.update(_to_bytes(s)) - - return Seed.from_bytes(h.digest()) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/random/random_number.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/random/random_number.py deleted file mode 100644 index edebed7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/random/random_number.py +++ /dev/null @@ -1,35 +0,0 @@ -from random import Random - -from nodes.groups import seed_group -from nodes.properties.inputs import NumberInput, SeedInput -from nodes.properties.outputs import NumberOutput -from nodes.utils.seed import Seed - -from .. import random_group - - -@random_group.register( - schema_id="chainner:utility:random_number", - name="Random Number", - description="Generate a random integer.", - icon="MdCalculate", - inputs=[ - NumberInput( - "Min", - minimum=None, - maximum=None, - ), - NumberInput( - "Max", - minimum=None, - maximum=None, - default=100, - ), - seed_group(SeedInput()), - ], - outputs=[ - NumberOutput("Result", output_type="int & max(.., Input0) & min(.., Input1)") - ], -) -def random_number_node(min_val: int, max_val: int, seed: Seed) -> int: - return Random(seed.value).randint(min_val, max_val) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/note.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/note.py deleted file mode 100644 index 310bd02..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/note.py +++ /dev/null @@ -1,29 +0,0 @@ -from __future__ import annotations - -from nodes.properties.inputs import BoolInput, TextInput - -from .. import text_group - - -# This node is a bit special as it has special handling by the frontend. Changes made here will not necessarily be reflected in the frontend. -@text_group.register( - schema_id="chainner:utility:note", - name="Note", - description="Make a sticky note for whatever notes or comments you want to leave in the chain. Supports markdown syntax", - icon="MdOutlineStickyNote2", - inputs=[ - TextInput( - label="Note Text", - multiline=True, - has_handle=False, - label_style="hidden", - ).make_optional(), - BoolInput( - label="Display Markdown", - default=False, - ), - ], - outputs=[], -) -def note_node(_text: str | None, display_markdown: bool) -> None: - return diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/regex_replace.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/regex_replace.py deleted file mode 100644 index 108bdb6..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/regex_replace.py +++ /dev/null @@ -1,92 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from chainner_ext import MatchGroup, RustRegex - -from nodes.properties.inputs import EnumInput, TextInput -from nodes.properties.outputs import TextOutput -from nodes.utils.replacement import ReplacementString - -from .. import text_group - - -class ReplacementMode(Enum): - REPLACE_ALL = 0 - REPLACE_FIRST = 1 - - -@text_group.register( - schema_id="chainner:utility:regex_replace", - name="Regex Replace", - description="Replace occurrences of some regex with a replacement text. Either or all occurrences or the first occurrence will be replaced", - icon="MdTextFields", - inputs=[ - TextInput("Text"), - TextInput("Regex", placeholder=r'E.g. "\b\w+\b"'), - TextInput( - "Replacement Pattern", - allow_empty_string=True, - placeholder=r'E.g. "found {0}"', - ), - EnumInput( - ReplacementMode, - label="Replace mode", - default=ReplacementMode.REPLACE_ALL, - ), - ], - outputs=[ - TextOutput( - "Text", - output_type=""" - let count = match Input3 { - ReplacementMode::ReplaceAll => inf, - ReplacementMode::ReplaceFirst => 1, - }; - regexReplace(Input0, Input1, Input2, count) - """, - ).with_never_reason( - "Either the regex pattern or the replacement pattern is invalid" - ), - ], - see_also=["chainner:utility:text_replace"], -) -def regex_replace_node( - text: str, - regex_pattern: str, - replacement_pattern: str, - mode: ReplacementMode, -) -> str: - # parse the inputs before we do any actual work - r = RustRegex(regex_pattern) - replacement = ReplacementString(replacement_pattern) - - matches = r.findall(text) - if len(matches) == 0: - return text - - if mode == ReplacementMode.REPLACE_FIRST: - matches = matches[:1] - - def get_group_text(group: MatchGroup | None) -> str: - if group is not None: - return text[group.start : group.end] - else: - return "" - - result = "" - last_end = 0 - for match in matches: - result += text[last_end : match.start] - - replacements: dict[str, str] = {} - for i in range(r.groups + 1): - replacements[str(i)] = get_group_text(match.get(i)) - for name, i in r.groupindex.items(): - replacements[name] = get_group_text(match.get(i)) - - result += replacement.replace(replacements) - last_end = match.end - - result += text[last_end:] - return result diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_append.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_append.py deleted file mode 100644 index 1aed012..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_append.py +++ /dev/null @@ -1,59 +0,0 @@ -from __future__ import annotations - -from nodes.groups import optional_list_group -from nodes.properties.inputs import TextInput -from nodes.properties.outputs import TextOutput -from nodes.utils.utils import ALPHABET - -from .. import text_group - - -@text_group.register( - schema_id="chainner:utility:text_append", - name="Text Append", - description=[ - "Append different text together using a separator string.", - "This is the simplest method to join/concatenate text strings together.", - ], - icon="MdTextFields", - inputs=[ - TextInput( - "Separator", - has_handle=False, - min_length=0, - max_length=3, - default="-", - allow_empty_string=True, - ), - TextInput("Text A").suggest(), - TextInput("Text B"), - optional_list_group( - *[TextInput(f"Text {letter}").make_optional() for letter in ALPHABET[2:10]], - ), - ], - outputs=[ - TextOutput( - "Output Text", - output_type=""" - let sep = Input0; - string::concat( - Input1, - sep, - Input2, - match Input3 { null => "", _ as s => string::concat(sep, s) }, - match Input4 { null => "", _ as s => string::concat(sep, s) }, - match Input5 { null => "", _ as s => string::concat(sep, s) }, - match Input6 { null => "", _ as s => string::concat(sep, s) }, - match Input7 { null => "", _ as s => string::concat(sep, s) }, - match Input8 { null => "", _ as s => string::concat(sep, s) }, - match Input9 { null => "", _ as s => string::concat(sep, s) }, - match Input10 { null => "", _ as s => string::concat(sep, s) } - ) - """, - ).suggest() - ], - see_also=["chainner:utility:text_pattern"], -) -def text_append_node(separator: str, *args: str | None) -> str: - inputs: list[str | None] = [*args] - return separator.join([x for x in inputs if x is not None]) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_length.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_length.py deleted file mode 100644 index ed84c7f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_length.py +++ /dev/null @@ -1,22 +0,0 @@ -from __future__ import annotations - -from nodes.properties.inputs import TextInput -from nodes.properties.outputs import NumberOutput - -from .. import text_group - - -@text_group.register( - schema_id="chainner:utility:text_length", - name="Text Length", - description="Returns the number characters in a string of text.", - icon="MdTextFields", - inputs=[ - TextInput("Text"), - ], - outputs=[ - NumberOutput("Length", output_type="string::len(Input0)"), - ], -) -def text_length_node(text: str) -> int: - return len(text) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_padding.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_padding.py deleted file mode 100644 index 58dc9f7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_padding.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from nodes.properties.inputs import EnumInput, NumberInput, TextInput -from nodes.properties.outputs import TextOutput - -from .. import text_group - - -class PaddingAlignment(Enum): - START = "start" - END = "end" - CENTER = "center" - - -@text_group.register( - schema_id="chainner:utility:text_padding", - name="Text Padding", - description="Pads text until it has a certain length.", - icon="MdTextFields", - inputs=[ - TextInput("Text"), - NumberInput("Width", unit="chars"), - TextInput( - "Padding Character", - has_handle=False, - allow_numbers=False, - min_length=1, - max_length=1, - placeholder="e.g. '0' or ' '", - ), - EnumInput(PaddingAlignment, label="Alignment"), - ], - outputs=[ - TextOutput( - "Output Text", - output_type=""" - match Input3 { - PaddingAlignment::Start => padStart(Input0, Input1, Input2), - PaddingAlignment::End => padEnd(Input0, Input1, Input2), - PaddingAlignment::Center => padCenter(Input0, Input1, Input2), - } - """, - ) - ], -) -def text_padding_node( - text: str, width: int, padding: str, alignment: PaddingAlignment -) -> str: - if alignment == PaddingAlignment.START: - return text.rjust(width, padding) - elif alignment == PaddingAlignment.END: - return text.ljust(width, padding) - elif alignment == PaddingAlignment.CENTER: - return text.center(width, padding) - else: - raise ValueError(f"Invalid alignment '{alignment}'.") diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_pattern.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_pattern.py deleted file mode 100644 index da0adc5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_pattern.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from nodes.groups import optional_list_group -from nodes.properties.inputs import TextInput -from nodes.properties.outputs import TextOutput -from nodes.utils.replacement import ReplacementString - -from .. import text_group - - -@text_group.register( - schema_id="chainner:utility:text_pattern", - name="Text Pattern", - description="Concatenate text using a pattern with a Python-like string interpolation syntax.", - icon="MdTextFields", - inputs=[ - TextInput("Pattern", has_handle=False, placeholder='E.g. "{1} and {2}"'), - TextInput("{1}").make_optional(), - TextInput("{2}").make_optional(), - optional_list_group( - *[TextInput(f"{{{number}}}").make_optional() for number in range(3, 10)], - ), - ], - outputs=[ - TextOutput( - "Output Text", - output_type=""" - formatPattern( - Input0, - Input1, - Input2, - Input3, - Input4, - Input5, - Input6, - Input7, - Input8, - Input9 - ) - """, - ).with_never_reason( - "The pattern is either syntactically invalid or contains replacements that do not have a value." - '\n\nHint: Use "{{" to escape a single "{" inside the pattern.' - ) - ], - see_also=["chainner:utility:text_append"], -) -def text_pattern_node( - pattern: str, - *args: str | None, -) -> str: - replacements: dict[str, str] = {} - for i, s in enumerate(args): - if s is not None: - replacements[str(i + 1)] = s - - return ReplacementString(pattern).replace(replacements) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_replace.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_replace.py deleted file mode 100644 index 7f9eb99..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_replace.py +++ /dev/null @@ -1,49 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from nodes.properties.inputs import EnumInput, TextInput -from nodes.properties.outputs import TextOutput - -from .. import text_group - - -class ReplacementMode(Enum): - REPLACE_ALL = 0 - REPLACE_FIRST = 1 - - -@text_group.register( - schema_id="chainner:utility:text_replace", - name="Text Replace", - description="Replace occurrences of some text with a replacement text. Either or all occurrences or the first occurrence will be replaced", - icon="MdTextFields", - inputs=[ - TextInput("Text"), - TextInput("Old Text"), - TextInput("Replacement", allow_empty_string=True), - EnumInput( - ReplacementMode, - label="Replace mode", - default=ReplacementMode.REPLACE_ALL, - ), - ], - outputs=[ - TextOutput( - "Text", - output_type=""" - let count = match Input3 { - ReplacementMode::ReplaceAll => inf, - ReplacementMode::ReplaceFirst => 1, - }; - string::replace(Input0, Input1, Input2, count) - """, - ), - ], - see_also=["chainner:utility:regex_replace"], -) -def text_replace_node(text: str, old: str, new: str, mode: ReplacementMode) -> str: - if mode == ReplacementMode.REPLACE_ALL: - return text.replace(old, new) - else: - return text.replace(old, new, 1) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_slice.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_slice.py deleted file mode 100644 index f222764..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/text/text_slice.py +++ /dev/null @@ -1,100 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from nodes.groups import if_enum_group -from nodes.properties.inputs import EnumInput, NumberInput, TextInput -from nodes.properties.outputs import TextOutput - -from .. import text_group - - -class SliceOperation(Enum): - START = 0 - START_AND_LENGTH = 1 - MAX_LENGTH = 2 - - -class SliceAlignment(Enum): - START = "start" - END = "end" - - -@text_group.register( - schema_id="chainner:utility:text_slice", - name="Text Slice", - description="Creates a slice of a given string of text.", - icon="MdTextFields", - inputs=[ - TextInput("Text"), - EnumInput( - SliceOperation, - label="Operation", - default=SliceOperation.START, - option_labels={ - SliceOperation.START: "Start", - SliceOperation.START_AND_LENGTH: "Start & Length", - SliceOperation.MAX_LENGTH: "Maximum Length", - }, - ).with_id(1), - if_enum_group(1, (SliceOperation.START, SliceOperation.START_AND_LENGTH))( - NumberInput("Start", minimum=None, maximum=None, unit="chars"), - ), - if_enum_group(1, SliceOperation.START_AND_LENGTH)( - NumberInput("Length", minimum=0, maximum=None, unit="chars"), - ), - if_enum_group(1, SliceOperation.MAX_LENGTH)( - NumberInput("Maximum Length", minimum=0, maximum=None, unit="chars"), - EnumInput(SliceAlignment, label="Alignment"), - ), - ], - outputs=[ - TextOutput( - "Output Text", - output_type=""" - let text = Input0; - let operation = Input1; - let start = Input2; - let length = Input3; - let maxLength = Input4; - let alignment = Input5; - - match operation { - SliceOperation::Start => string::slice(text, start, inf), - SliceOperation::StartAndLength => string::slice(text, start, length), - SliceOperation::MaxLength => { - match alignment { - SliceAlignment::Start => string::slice(text, 0, maxLength), - SliceAlignment::End => { - match maxLength { - 0 => "", - _ as maxLength => string::slice(text, -maxLength, inf), - } - }, - } - }, - } - """, - ) - ], -) -def text_slice_node( - text: str, - operation: SliceOperation, - start: int, - length: int, - max_length: int, - alignment: SliceAlignment, -) -> str: - if operation == SliceOperation.START: - return text[start:] - elif operation == SliceOperation.START_AND_LENGTH: - start = max(-len(text), start) - return text[start : start + length] - elif operation == SliceOperation.MAX_LENGTH: - if max_length == 0: - return "" - if alignment == SliceAlignment.START: - return text[:max_length] - elif alignment == SliceAlignment.END: - return text[-max_length:] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/directory.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/directory.py deleted file mode 100644 index cad077d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/directory.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import annotations - -from pathlib import Path - -from nodes.properties.inputs import DirectoryInput -from nodes.properties.outputs import DirectoryOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:directory", - name="Directory", - description="Outputs the given directory.", - icon="BsFolder", - inputs=[ - DirectoryInput(must_exist=False, label_style="hidden").make_fused(), - ], - outputs=[ - DirectoryOutput(output_type="Input0").suggest(), - ], -) -def directory_node(directory: Path) -> Path: - return directory diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/execution_number.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/execution_number.py deleted file mode 100644 index a2c35e5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/execution_number.py +++ /dev/null @@ -1,27 +0,0 @@ -from __future__ import annotations - -from nodes.properties.inputs.__system_inputs import StaticValueInput -from nodes.properties.outputs import NumberOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:execution_number", - name="Execution Number", - description="Get the current execution number of this session. Increments by 1 every time you press the run button.", - icon="MdNumbers", - inputs=[ - StaticValueInput( - label="Value", - value="execution_number", - navi_type="int(1..)", - py_type=int, - ).make_fused(), - ], - outputs=[ - NumberOutput("Execution Number", output_type="Input0"), - ], -) -def execution_number_node(number: int) -> int: - return number diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/number.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/number.py deleted file mode 100644 index 4a2bfd4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/number.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -from api import KeyInfo -from nodes.properties.inputs import NumberInput -from nodes.properties.outputs import NumberOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:number", - name="Number", - description="Outputs the given number.", - icon="MdCalculate", - inputs=[ - NumberInput( - "Number", - minimum=None, - maximum=None, - precision=100, - controls_step=1, - label_style="hidden", - ).make_fused(), - ], - outputs=[ - NumberOutput("Number", output_type="Input0").suggest(), - ], - key_info=KeyInfo.number(0), -) -def number_node(number: float) -> float: - return number diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/parse_number.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/parse_number.py deleted file mode 100644 index 94b8c09..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/parse_number.py +++ /dev/null @@ -1,26 +0,0 @@ -from __future__ import annotations - -from nodes.properties.inputs import NumberInput, TextInput -from nodes.properties.outputs import NumberOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:parse_number", - name="Parse Number", - description="Parses text to base-10.", - icon="MdCalculate", - inputs=[ - TextInput("Text", label_style="inline"), - NumberInput("Base", default=10, minimum=2, maximum=36), - ], - outputs=[ - NumberOutput( - "Value", - output_type="int & number::parseInt(Input0, Input1)", - ).with_never_reason("The given text cannot be parsed into a number."), - ], -) -def parse_number_node(text: str, base: int) -> int: - return int(text, base) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/pass_through.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/pass_through.py deleted file mode 100644 index 0743925..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/pass_through.py +++ /dev/null @@ -1,67 +0,0 @@ -from __future__ import annotations - -from nodes.groups import optional_list_group -from nodes.properties.inputs import AnyInput -from nodes.properties.outputs import AnyOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:pass_through", - name="Pass Through", - description="Outputs the input value as is. Supports up to 10 inputs.", - icon="MdDoubleArrow", - inputs=[ - AnyInput(label="Value 1").make_fused(0), - optional_list_group( - AnyInput(label="Value 2").make_fused(1).make_optional(), - AnyInput(label="Value 3").make_fused(2).make_optional(), - AnyInput(label="Value 4").make_fused(3).make_optional(), - AnyInput(label="Value 5").make_fused(4).make_optional(), - AnyInput(label="Value 6").make_fused(5).make_optional(), - AnyInput(label="Value 7").make_fused(6).make_optional(), - AnyInput(label="Value 8").make_fused(7).make_optional(), - AnyInput(label="Value 9").make_fused(8).make_optional(), - AnyInput(label="Value 10").make_fused(9).make_optional(), - ), - ], - outputs=[ - AnyOutput(output_type="Input0", label="Value 1"), - AnyOutput(output_type="Input1", label="Value 2"), - AnyOutput(output_type="Input2", label="Value 3"), - AnyOutput(output_type="Input3", label="Value 4"), - AnyOutput(output_type="Input4", label="Value 5"), - AnyOutput(output_type="Input5", label="Value 6"), - AnyOutput(output_type="Input6", label="Value 7"), - AnyOutput(output_type="Input7", label="Value 8"), - AnyOutput(output_type="Input8", label="Value 9"), - AnyOutput(output_type="Input9", label="Value 10"), - ], -) -def pass_through_node( - value_a: object, - value_b: object | None = None, - value_c: object | None = None, - value_d: object | None = None, - value_e: object | None = None, - value_f: object | None = None, - value_g: object | None = None, - value_h: object | None = None, - value_i: object | None = None, - value_j: object | None = None, -) -> tuple[ - object, object, object, object, object, object, object, object, object, object -]: - return ( - value_a, - value_b, - value_c, - value_d, - value_e, - value_f, - value_g, - value_h, - value_i, - value_j, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/percent.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/percent.py deleted file mode 100644 index 30888cb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/percent.py +++ /dev/null @@ -1,32 +0,0 @@ -from __future__ import annotations - -from api import KeyInfo -from nodes.properties.inputs import SliderInput -from nodes.properties.outputs import NumberOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:percent", - name="Percent", - description="Outputs the given percent.", - icon="MdCalculate", - inputs=[ - SliderInput( - "Percent", - minimum=0, - maximum=100, - default=50, - precision=0, - controls_step=1, - unit="%", - ).make_fused(), - ], - outputs=[ - NumberOutput("Percent", output_type="Input0"), - ], - key_info=KeyInfo.number(0), -) -def percent_node(number: int) -> int: - return number diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/range.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/range.py deleted file mode 100644 index d19a4d5..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/range.py +++ /dev/null @@ -1,54 +0,0 @@ -from __future__ import annotations - -from api import Iterator, IteratorOutputInfo, KeyInfo -from nodes.properties.inputs import BoolInput, NumberInput -from nodes.properties.outputs import NumberOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:range", - name="Range", - description="Iterates through all integers in the given range.", - icon="MdCalculate", - inputs=[ - NumberInput("Start", default=0, minimum=None, maximum=None), - BoolInput("Start Inclusive", default=True), - NumberInput("Stop", default=10, minimum=None, maximum=None), - BoolInput("Stop Inclusive", default=False), - ], - outputs=[ - NumberOutput( - "Number", - output_type=""" - let start = if Input1 { Input0 } else { Input0 + 1 }; - let stop = if Input3 { Input2 } else { Input2 - 1 }; - - max(int, start) & min(int, stop) - """, - ).with_never_reason("The range is empty."), - ], - key_info=KeyInfo.type( - """ - let start = if Input1 { Input0 } else { Input0 + 1 }; - let stop = if Input3 { Input2 } else { Input2 - 1 }; - - string::concat(toString(start), "..", toString(stop)) - """ - ), - iterator_outputs=IteratorOutputInfo(outputs=0), - kind="newIterator", -) -def range_node( - start: int, - start_inclusive: bool, - end: int, - end_inclusive: bool, -) -> Iterator[int]: - if not start_inclusive: - start += 1 - if end_inclusive: - end += 1 - count = end - start - return Iterator.from_range(count, lambda i: start + i) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/resolutions.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/resolutions.py deleted file mode 100644 index c257bd2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/resolutions.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from nodes.groups import if_enum_group -from nodes.properties.inputs import EnumInput, NumberInput -from nodes.properties.outputs import NumberOutput - -from .. import value_group - - -class ResList(Enum): - HD_1080 = "HD_1080" - NTSC_MPEG2 = "NTSC MPEG2" - NTSC_DV = "NTSC_DV" - NTSC = "NTSC" - PAL = "PAL" - PAL_MPEG2 = "PAL MPEG2" - HD_720 = "HD_720" - UHD_4K = "UHD_4K" - UHD2_8K = "UHD2_8K" - DCI_2KFLAT = "DCI_2KFLAT" - DCI_2KSCOPE = "DCI_2KSCOPE" - DCI_2KFULL = "DCI_2K1080" - DCI_4KFLAT = "DCI_4KFLAT" - DCI_4KSCOPE = "DCI_4KSCOPE" - DCI_4KFULL = "DCI_4K2160" - SQ256 = "SQ256" - SQ512 = "SQ512" - SQ1024 = "SQ1024" - SQ2048 = "SQ2048" - SQ4096 = "SQ4096" - SQ8192 = "SQ8192" - CUSTOM = "CUSTOM" - - -RESOLUTIONS: dict[ResList, tuple[int, int]] = { - ResList.HD_1080: (1920, 1080), - ResList.NTSC_MPEG2: (704, 480), - ResList.NTSC: (720, 486), - ResList.NTSC_DV: (720, 480), - ResList.PAL: (720, 576), - ResList.PAL_MPEG2: (702, 576), - ResList.HD_720: (1280, 720), - ResList.UHD_4K: (3840, 2160), - ResList.UHD2_8K: (7680, 4320), - ResList.DCI_2KFLAT: (1998, 1080), - ResList.DCI_2KSCOPE: (2048, 858), - ResList.DCI_2KFULL: (2048, 1080), - ResList.DCI_4KFULL: (4096, 2160), - ResList.DCI_4KFLAT: (3996, 2160), - ResList.DCI_4KSCOPE: (4096, 1716), - ResList.SQ256: (256, 256), - ResList.SQ512: (512, 512), - ResList.SQ1024: (1024, 1024), - ResList.SQ2048: (2048, 2048), - ResList.SQ4096: (4096, 4096), - ResList.SQ8192: (8192, 8192), - ResList.CUSTOM: (1920, 1080), -} - -assert len(RESOLUTIONS) == len(ResList) - - -@value_group.register( - schema_id="chainner:utility:resolutions", - name="Resolutions", - description="A collection of commonly used image resolution presets.", - icon="BsBadgeHd", - inputs=[ - EnumInput( - ResList, - label="Resolution Presets", - default=ResList.HD_1080, - option_labels={ - ResList.HD_1080: "HD 1080P 1920x1080", - ResList.NTSC_MPEG2: "SD NTSC MPEG-2 DVD 704x480", - ResList.NTSC: "SD NTSC 720x486", - ResList.NTSC_DV: "SD NTSC DV 720x480", - ResList.PAL: "SD PAL 720x576", - ResList.PAL_MPEG2: "SD PAL MPEG-2 DVD 702x576", - ResList.HD_720: "HD 720P 1280x720", - ResList.UHD_4K: "UHD 4K 3840x2160", - ResList.UHD2_8K: "UHD2 8K 7680x4320", - ResList.DCI_2KFLAT: "2K DCI FLAT 1998x1080", - ResList.DCI_2KSCOPE: "2K DCI SCOPE 2048x858", - ResList.DCI_2KFULL: "2K DCI FULL 2048x1080", - ResList.DCI_4KFULL: "4K DCI FULL 4096x2160", - ResList.DCI_4KFLAT: "4K DCI FLAT 3996x2160", - ResList.DCI_4KSCOPE: "4K DCI SCOPE 4096x1716", - ResList.SQ256: "Square 256x256", - ResList.SQ512: "Square 512x512", - ResList.SQ1024: "Square 1024x1024", - ResList.SQ2048: "Square 2048x2048", - ResList.SQ4096: "Square 4096x4096", - ResList.SQ8192: "Square 8192x8192", - ResList.CUSTOM: "Custom Resolution", - }, - ), - if_enum_group(0, ResList.CUSTOM)( - NumberInput( - "Width", - minimum=1, - maximum=None, - default=1920, - precision=0, - controls_step=1, - unit="px", - has_handle=False, - ), - NumberInput( - "Height", - minimum=1, - maximum=None, - default=1080, - precision=0, - controls_step=1, - unit="px", - has_handle=False, - ), - ), - ], - outputs=[ - NumberOutput("Width", output_type="int(1..)"), - NumberOutput("Height", output_type="int(1..)"), - ], -) -def resolutions_node( - resolution_presets: ResList, - width: int, - height: int, -) -> tuple[int, int]: - if resolution_presets == ResList.CUSTOM: - return width, height - else: - return RESOLUTIONS[resolution_presets] diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/switch.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/switch.py deleted file mode 100644 index c22079e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/switch.py +++ /dev/null @@ -1,76 +0,0 @@ -from __future__ import annotations - -from enum import Enum - -from api import KeyInfo -from nodes.groups import optional_list_group -from nodes.properties.inputs import AnyInput, EnumInput -from nodes.properties.outputs import BaseOutput -from nodes.utils.utils import ALPHABET - -from .. import value_group - - -class ValueIndex(Enum): - A = 0 - B = 1 - C = 2 - D = 3 - E = 4 - F = 5 - G = 6 - H = 7 - I = 8 - J = 9 - - -@value_group.register( - schema_id="chainner:utility:switch", - name="Switch", - description="Allows you to pass in multiple inputs and then change which one passes through to the output.", - icon="BsShuffle", - inputs=[ - EnumInput(ValueIndex).with_id(0), - AnyInput(label="Value A"), - AnyInput(label="Value B"), - optional_list_group( - *[ - AnyInput(label=f"Value {letter}").make_optional() - for letter in ALPHABET[2:10] - ], - ), - ], - outputs=[ - BaseOutput( - output_type=""" - let value = match Input0 { - ValueIndex::A => Input1, - ValueIndex::B => Input2, - ValueIndex::C => Input3, - ValueIndex::D => Input4, - ValueIndex::E => Input5, - ValueIndex::F => Input6, - ValueIndex::G => Input7, - ValueIndex::H => Input8, - ValueIndex::I => Input9, - ValueIndex::J => Input10, - _ => never - }; - - match value { - null => never, - _ => value - } - """, - label="Value", - ).with_never_reason("The selected value should have a connection.") - ], - key_info=KeyInfo.enum(0), - see_also=["chainner:utility:pass_through"], -) -def switch_node(selection: ValueIndex, *args: object | None) -> object: - if args[selection.value] is not None: - return args[selection.value] - raise RuntimeError( - "Invalid value selected. The selected value should have a connection." - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/text.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/text.py deleted file mode 100644 index 7b29393..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/packages/chaiNNer_standard/utility/value/text.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import annotations - -from nodes.properties.inputs import TextInput -from nodes.properties.outputs import TextOutput - -from .. import value_group - - -@value_group.register( - schema_id="chainner:utility:text", - name="Text", - description="Outputs the given text.", - icon="MdTextFields", - inputs=[ - TextInput( - "Text", min_length=0, label_style="hidden", allow_empty_string=True - ).make_fused(), - ], - outputs=[ - TextOutput("Text", output_type="Input0").suggest(), - ], -) -def text_node(text: str) -> str: - return text diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/process.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/process.py deleted file mode 100644 index 12049c3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/process.py +++ /dev/null @@ -1,950 +0,0 @@ -from __future__ import annotations - -import asyncio -import functools -import gc -import time -from concurrent.futures import ThreadPoolExecutor -from contextlib import contextmanager -from dataclasses import dataclass -from pathlib import Path -from typing import Iterable, List, NewType, Sequence, Union - -from sanic.log import logger - -from api import ( - BaseInput, - BaseOutput, - Collector, - ExecutionOptions, - InputId, - Iterator, - Lazy, - NodeContext, - NodeData, - NodeId, - OutputId, - SettingsParser, - registry, -) -from chain.cache import CacheStrategy, OutputCache, StaticCaching, get_cache_strategies -from chain.chain import Chain, CollectorNode, FunctionNode, NewIteratorNode, Node -from chain.input import EdgeInput, Input, InputMap -from events import EventConsumer, InputsDict -from progress_controller import Aborted, ProgressController, ProgressToken -from util import timed_supplier - -Output = List[object] - - -def collect_input_information( - node: NodeData, - inputs: list[object | Lazy[object]], - enforced: bool = True, -) -> InputsDict: - try: - input_dict: InputsDict = {} - - for value, node_input in zip(inputs, node.inputs): - if isinstance(value, Lazy) and value.has_value: - value = value.value # noqa: PLW2901 - - if isinstance(value, Lazy): - # the value hasn't been computed yet, so we won't do so here - input_dict[node_input.id] = {"type": "pending"} - continue - - if not enforced: - try: - value = node_input.enforce_(value) # noqa - except Exception: - logger.error( - f"Error enforcing input {node_input.label} (id {node_input.id})", - exc_info=True, - ) - # We'll just try using the un-enforced value. Maybe it'll work. - - try: - input_dict[node_input.id] = node_input.get_error_value(value) - except Exception: - logger.error( - f"Error getting error value for input {node_input.label} (id {node_input.id})", - exc_info=True, - ) - - return input_dict - except Exception: - # this method must not throw - logger.error("Error collecting input information.", exc_info=True) - return {} - - -def enforce_inputs( - inputs: list[object], - node: NodeData, - node_id: NodeId, - ignored_inputs: list[InputId], -) -> list[object]: - def enforce(i: BaseInput, value: object) -> object: - if i.id in ignored_inputs: - return None - - # we generally assume that enforcing a value is cheap, so we do it as soon as possible - if i.lazy: - if isinstance(value, Lazy): - return Lazy(lambda: i.enforce_(value.value)) - return Lazy.ready(i.enforce_(value)) - - if isinstance(value, Lazy): - value = value.value # compute lazy value - return i.enforce_(value) - - try: - enforced_inputs: list[object] = [] - for index, value in enumerate(inputs): - enforced_inputs.append(enforce(node.inputs[index], value)) - return enforced_inputs - except Exception as e: - input_dict = collect_input_information(node, inputs, enforced=False) - raise NodeExecutionError(node_id, node, str(e), input_dict) from e - - -def enforce_output(raw_output: object, node: NodeData) -> RegularOutput: - l = len(node.outputs) - - output: Output - if l == 0: - assert raw_output is None, f"Expected all {node.name} nodes to return None." - output = [] - elif l == 1: - output = [raw_output] - else: - assert isinstance(raw_output, (tuple, list)) - output = list(raw_output) - assert ( - len(output) == l - ), f"Expected all {node.name} nodes to have {l} output(s) but found {len(output)}." - - # output-specific validations - for i, o in enumerate(node.outputs): - output[i] = o.enforce(output[i]) - - return RegularOutput(output) - - -def enforce_iterator_output(raw_output: object, node: NodeData) -> IteratorOutput: - l = len(node.outputs) - iterator_output = node.single_iterator_output - - partial: list[object] = [None] * l - - if l == len(iterator_output.outputs): - assert isinstance(raw_output, Iterator), "Expected the output to be an iterator" - return IteratorOutput(iterator=raw_output, partial_output=partial) - - assert l > len(iterator_output.outputs) - assert isinstance(raw_output, (tuple, list)) - - iterator, *rest = raw_output - assert isinstance( - iterator, Iterator - ), "Expected the first tuple element to be an iterator" - assert len(rest) == l - len(iterator_output.outputs) - - # output-specific validations - for i, o in enumerate(node.outputs): - if o.id not in iterator_output.outputs: - partial[i] = o.enforce(rest.pop(0)) - - return IteratorOutput(iterator=iterator, partial_output=partial) - - -def run_node( - node: NodeData, context: NodeContext, inputs: list[object], node_id: NodeId -) -> NodeOutput | CollectorOutput: - if node.kind == "collector": - ignored_inputs = node.single_iterator_input.inputs - else: - ignored_inputs = [] - - enforced_inputs = enforce_inputs(inputs, node, node_id, ignored_inputs) - - try: - if node.node_context: - raw_output = node.run(context, *enforced_inputs) - else: - raw_output = node.run(*enforced_inputs) - - if node.kind == "collector": - assert isinstance(raw_output, Collector) - return CollectorOutput(raw_output) - if node.kind == "newIterator": - return enforce_iterator_output(raw_output, node) - - assert node.kind == "regularNode" - return enforce_output(raw_output, node) - except Aborted: - raise - except NodeExecutionError: - raise - except Exception as e: - # collect information to provide good error messages - input_dict = collect_input_information(node, enforced_inputs) - raise NodeExecutionError(node_id, node, str(e), input_dict) from e - - -def run_collector_iterate( - node: CollectorNode, inputs: list[object], collector: Collector -) -> None: - iterator_input = node.data.single_iterator_input - - def get_partial_inputs(values: list[object]) -> list[object]: - partial_inputs: list[object] = [] - index = 0 - for i in node.data.inputs: - if i.id in iterator_input.inputs: - partial_inputs.append(values[index]) - index += 1 - else: - partial_inputs.append(None) - return partial_inputs - - enforced_inputs: list[object] = [] - try: - for i in node.data.inputs: - if i.id in iterator_input.inputs: - enforced_inputs.append(i.enforce_(inputs[len(enforced_inputs)])) - except Exception as e: - input_dict = collect_input_information( - node.data, get_partial_inputs(inputs), enforced=False - ) - raise NodeExecutionError(node.id, node.data, str(e), input_dict) from e - - input_value = ( - enforced_inputs[0] if len(enforced_inputs) == 1 else tuple(enforced_inputs) - ) - - try: - raw_output = collector.on_iterate(input_value) - assert raw_output is None - except Exception as e: - input_dict = collect_input_information( - node.data, get_partial_inputs(enforced_inputs) - ) - raise NodeExecutionError(node.id, node.data, str(e), input_dict) from e - - -class _Timer: - def __init__(self) -> None: - self.duration: float = 0 - - @contextmanager - def run(self): - start = time.monotonic() - try: - yield None - finally: - self.add_since(start) - - def add_since(self, start: float): - self.duration += time.monotonic() - start - - -class _IterationTimer: - def __init__(self, progress: ProgressController) -> None: - self.times: list[float] = [] - self.progress = progress - - self._start_time = time.monotonic() - self._start_paused = progress.time_paused - - self._last_time = self._start_time - self._last_paused = self._start_paused - - @property - def iterations(self) -> int: - return len(self.times) - - def get_time_since_start(self) -> float: - now = time.monotonic() - paused = self.progress.time_paused - - current_paused = max(0, paused - self._start_paused) - return now - self._start_time - current_paused - - def add(self): - now = time.monotonic() - paused = self.progress.time_paused - - current_paused = max(0, paused - self._last_paused) - self.times.append(now - self._last_time - current_paused) - - self._last_time = now - self._last_paused = paused - - -def compute_broadcast(output: Output, node_outputs: Iterable[BaseOutput]): - data: dict[OutputId, object] = {} - types: dict[OutputId, object] = {} - for index, node_output in enumerate(node_outputs): - try: - value = output[index] - if value is not None: - data[node_output.id] = node_output.get_broadcast_data(value) - types[node_output.id] = node_output.get_broadcast_type(value) - except Exception as e: - logger.error(f"Error broadcasting output: {e}") - return data, types - - -class NodeExecutionError(Exception): - def __init__( - self, - node_id: NodeId, - node_data: NodeData, - cause: str, - inputs: InputsDict, - ): - super().__init__(cause) - self.node_id: NodeId = node_id - self.node_data: NodeData = node_data - self.inputs: InputsDict = inputs - - -@dataclass(frozen=True) -class RegularOutput: - output: Output - - -@dataclass(frozen=True) -class IteratorOutput: - iterator: Iterator - partial_output: Output - - -@dataclass(frozen=True) -class CollectorOutput: - collector: Collector - - -NodeOutput = Union[RegularOutput, IteratorOutput] - -ExecutionId = NewType("ExecutionId", str) - - -class _ExecutorNodeContext(NodeContext): - def __init__( - self, progress: ProgressToken, settings: SettingsParser, storage_dir: Path - ) -> None: - super().__init__() - - self.progress = progress - self.__settings = settings - self._storage_dir = storage_dir - - @property - def aborted(self) -> bool: - return self.progress.aborted - - @property - def paused(self) -> bool: - time.sleep(0.001) - return self.progress.paused - - def set_progress(self, progress: float) -> None: - self.check_aborted() - - # TODO: send progress event - - @property - def settings(self) -> SettingsParser: - """ - Returns the settings of the current node execution. - """ - return self.__settings - - @property - def storage_dir(self) -> Path: - return self._storage_dir - - -class Executor: - """ - Class for executing chaiNNer's processing logic - """ - - def __init__( - self, - id: ExecutionId, - chain: Chain, - send_broadcast_data: bool, - options: ExecutionOptions, - loop: asyncio.AbstractEventLoop, - queue: EventConsumer, - pool: ThreadPoolExecutor, - storage_dir: Path, - parent_cache: OutputCache[NodeOutput] | None = None, - ): - self.id: ExecutionId = id - self.chain = chain - self.inputs: InputMap = InputMap.from_chain(chain) - self.send_broadcast_data: bool = send_broadcast_data - self.options: ExecutionOptions = options - self.cache: OutputCache[NodeOutput] = OutputCache(parent=parent_cache) - self.__broadcast_tasks: list[asyncio.Task[None]] = [] - self.__context_cache: dict[str, _ExecutorNodeContext] = {} - - self.progress = ProgressController() - - self.loop: asyncio.AbstractEventLoop = loop - self.queue: EventConsumer = queue - self.pool: ThreadPoolExecutor = pool - - self.cache_strategy: dict[NodeId, CacheStrategy] = get_cache_strategies(chain) - - self._storage_dir = storage_dir - - async def process(self, node_id: NodeId) -> NodeOutput | CollectorOutput: - # Return cached output value from an already-run node if that cached output exists - cached = self.cache.get(node_id) - if cached is not None: - return cached - - node = self.chain.nodes[node_id] - try: - return await self.__process(node) - except Aborted: - raise - except NodeExecutionError: - raise - except Exception as e: - raise NodeExecutionError(node.id, node.data, str(e), {}) from e - - async def process_regular_node(self, node: FunctionNode) -> RegularOutput: - """ - Processes the given regular node. - - This will run all necessary node events. - """ - result = await self.process(node.id) - assert isinstance(result, RegularOutput) - return result - - async def process_iterator_node(self, node: NewIteratorNode) -> IteratorOutput: - """ - Processes the given iterator node. - - This will **not** iterate the returned iterator. Only `node-start` and - `node-broadcast` events will be sent. - """ - result = await self.process(node.id) - assert isinstance(result, IteratorOutput) - return result - - async def process_collector_node(self, node: CollectorNode) -> CollectorOutput: - """ - Processes the given iterator node. - - This will **not** iterate the returned collector. Only a `node-start` event - will be sent. - """ - result = await self.process(node.id) - assert isinstance(result, CollectorOutput) - return result - - async def __get_node_output(self, node_id: NodeId, output_index: int) -> object: - """ - Returns the output value of the given node. - - Note: `output_index` is NOT an output ID. - """ - - # Recursively get the value of the input - output = await self.process(node_id) - - if isinstance(output, CollectorOutput): - # this generally shouldn't be possible - raise ValueError("A collector was not run before another node needed it.") - - if isinstance(output, IteratorOutput): - value = output.partial_output[output_index] - assert value is not None, "An iterator output was not assigned correctly" - return value - - assert isinstance(output, RegularOutput) - return output.output[output_index] - - async def __resolve_node_input(self, node_input: Input) -> object: - if isinstance(node_input, EdgeInput): - # If input is a dict indicating another node, use that node's output value - # Recursively get the value of the input - return await self.__get_node_output(node_input.id, node_input.index) - else: - # Otherwise, just use the given input (number, string, etc) - return node_input.value - - async def __gather_inputs(self, node: Node) -> list[object]: - """ - Returns the list of input values for the given node. - """ - - # we want to ignore some inputs if we are running a collector node - ignore: set[int] = set() - if isinstance(node, CollectorNode): - iterator_input = node.data.single_iterator_input - for input_index, i in enumerate(node.data.inputs): - if i.id in iterator_input.inputs: - ignore.add(input_index) - - # some inputs are lazy, so we want to lazily resolve them - lazy: set[int] = set() - for input_index, i in enumerate(node.data.inputs): - if i.lazy: - lazy.add(input_index) - - assigned_inputs = self.inputs.get(node.id) - assert len(assigned_inputs) == len(node.data.inputs) - - async def get_input_value(input_index: int, node_input: Input): - if input_index in ignore: - return None - - if input_index in lazy: - return Lazy.from_coroutine( - self.__resolve_node_input(assigned_inputs[input_index]), self.loop - ) - - return await self.__resolve_node_input(node_input) - - inputs = [] - for input_index, node_input in enumerate(assigned_inputs): - inputs.append(await get_input_value(input_index, node_input)) - - return inputs - - async def __gather_collector_inputs(self, node: CollectorNode) -> list[object]: - """ - Returns the input values to be consumed by `Collector.on_iterate`. - """ - - iterator_input = node.data.single_iterator_input - - assigned_inputs = self.inputs.get(node.id) - assert len(assigned_inputs) == len(node.data.inputs) - - inputs = [] - for input_index, node_input in enumerate(assigned_inputs): - i = node.data.inputs[input_index] - if i.id in iterator_input.inputs: - inputs.append(await self.__resolve_node_input(node_input)) - - return inputs - - def __get_node_context(self, node: Node) -> _ExecutorNodeContext: - context = self.__context_cache.get(node.data.schema_id, None) - if context is None: - package_id = registry.get_package(node.data.schema_id).id - settings = self.options.get_package_settings(package_id) - - context = _ExecutorNodeContext(self.progress, settings, self._storage_dir) - self.__context_cache[node.data.schema_id] = context - - return context - - async def __process(self, node: Node) -> NodeOutput | CollectorOutput: - """ - Process a single node. - - In the case of iterators and collectors, it will only run the node itself, - not the actual iteration or collection. - """ - - logger.debug(f"node: {node}") - logger.debug(f"Running node {node.id}") - - inputs = await self.__gather_inputs(node) - context = self.__get_node_context(node) - - await self.progress.suspend() - await self.__send_node_start(node) - await self.progress.suspend() - - output, execution_time = await self.loop.run_in_executor( - self.pool, - timed_supplier( - functools.partial(run_node, node.data, context, inputs, node.id) - ), - ) - await self.progress.suspend() - - if isinstance(output, RegularOutput): - await self.__send_node_broadcast(node, output.output) - await self.__send_node_finish(node, execution_time) - elif isinstance(output, IteratorOutput): - await self.__send_node_broadcast(node, output.partial_output) - # TODO: execution time - - # Cache the output of the node - if not isinstance(output, CollectorOutput): - self.cache.set(node.id, output, self.cache_strategy[node.id]) - - await self.progress.suspend() - - return output - - def __get_iterated_nodes( - self, node: NewIteratorNode - ) -> tuple[set[CollectorNode], set[FunctionNode], set[Node]]: - """ - Returns all collector and output nodes iterated by the given iterator node - """ - collectors: set[CollectorNode] = set() - output_nodes: set[FunctionNode] = set() - - seen: set[Node] = {node} - - def visit(n: Node): - if n in seen: - return - seen.add(n) - - if isinstance(n, CollectorNode): - collectors.add(n) - elif isinstance(n, NewIteratorNode): - raise ValueError("Nested iterators are not supported") - else: - assert isinstance(n, FunctionNode) - - if n.has_side_effects(): - output_nodes.add(n) - - # follow edges - for edge in self.chain.edges_from(n.id): - target_node = self.chain.nodes[edge.target.id] - visit(target_node) - - iterator_output = node.data.single_iterator_output - for edge in self.chain.edges_from(node.id): - # only follow iterator outputs - if edge.source.output_id in iterator_output.outputs: - target_node = self.chain.nodes[edge.target.id] - visit(target_node) - - return collectors, output_nodes, seen - - def __iterator_fill_partial_output( - self, node: NewIteratorNode, partial_output: Output, values: object - ) -> Output: - iterator_output = node.data.single_iterator_output - - values_list: list[object] = [] - if len(iterator_output.outputs) == 1: - values_list.append(values) - else: - assert isinstance(values, (tuple, list)) - values_list.extend(values) - - assert len(values_list) == len(iterator_output.outputs) - - output: Output = partial_output.copy() - for index, o in enumerate(node.data.outputs): - if o.id in iterator_output.outputs: - output[index] = o.enforce(values_list.pop(0)) - - return output - - async def __iterate_iterator_node(self, node: NewIteratorNode): - await self.progress.suspend() - - # run the iterator node itself before anything else - iterator_output = await self.process_iterator_node(node) - - collector_nodes, output_nodes, all_iterated_nodes = self.__get_iterated_nodes( - node - ) - all_iterated_nodes = {n.id for n in all_iterated_nodes} - - if len(collector_nodes) == 0 and len(output_nodes) == 0: - # unusual, but this can happen - # since we don't need to actually iterate the iterator, we can stop here - return - - def fill_partial_output(values: object) -> RegularOutput: - return RegularOutput( - self.__iterator_fill_partial_output( - node, iterator_output.partial_output, values - ) - ) - - # run each of the collector nodes - collectors: list[tuple[Collector, _Timer, CollectorNode]] = [] - for collector_node in collector_nodes: - await self.progress.suspend() - timer = _Timer() - with timer.run(): - collector_output = await self.process_collector_node(collector_node) - assert isinstance(collector_output, CollectorOutput) - collectors.append((collector_output.collector, timer, collector_node)) - - # timing iterations - iter_times = _IterationTimer(self.progress) - expected_length = iterator_output.iterator.expected_length - - async def update_progress(): - iter_times.add() - iterations = iter_times.iterations - await self.__send_node_progress( - node, - iter_times.times, - iterations, - max(expected_length, iterations), - ) - - # iterate - await self.__send_node_progress(node, [], 0, expected_length) - - deferred_errors: list[str] = [] - for values in iterator_output.iterator.iter_supplier(): - try: - if isinstance(values, Exception): - raise values - - # write current values to cache - iter_output = fill_partial_output(values) - self.cache.set(node.id, iter_output, StaticCaching) - - # broadcast - await self.__send_node_broadcast(node, iter_output.output) - - # run each of the output nodes - for output_node in output_nodes: - await self.process_regular_node(output_node) - - # run each of the collector nodes - for collector, timer, collector_node in collectors: - await self.progress.suspend() - iterate_inputs = await self.__gather_collector_inputs( - collector_node - ) - await self.progress.suspend() - with timer.run(): - run_collector_iterate(collector_node, iterate_inputs, collector) - - # clear cache for next iteration - self.cache.delete_many(all_iterated_nodes) - - await self.progress.suspend() - await update_progress() - # cooperative yield so the event loop can run - # https://stackoverflow.com/questions/36647825/cooperative-yield-in-asyncio - await asyncio.sleep(0) - await self.progress.suspend() - except Aborted: - raise - except Exception as e: - if iterator_output.iterator.fail_fast: - raise e - else: - deferred_errors.append(str(e)) - - # reset cached value - self.cache.delete_many(all_iterated_nodes) - self.cache.set(node.id, iterator_output, self.cache_strategy[node.id]) - - # re-broadcast final value - # TODO: Why? - await self.__send_node_broadcast(node, iterator_output.partial_output) - - # finish iterator - await self.__send_node_progress_done(node, iter_times.iterations) - await self.__send_node_finish(node, iter_times.get_time_since_start()) - - # finalize collectors - for collector, timer, collector_node in collectors: - await self.progress.suspend() - with timer.run(): - collector_output = enforce_output( - collector.on_complete(), collector_node.data - ) - - await self.__send_node_broadcast(collector_node, collector_output.output) - # TODO: execution time - await self.__send_node_finish(collector_node, timer.duration) - - self.cache.set( - collector_node.id, - collector_output, - self.cache_strategy[collector_node.id], - ) - - if len(deferred_errors) > 0: - error_string = "- " + "\n- ".join(deferred_errors) - raise Exception(f"Errors occurred during iteration:\n{error_string}") - - async def __process_nodes(self): - await self.__send_chain_start() - - # we first need to run iterator nodes in topological order - for node_id in self.chain.topological_order(): - node = self.chain.nodes[node_id] - if isinstance(node, NewIteratorNode): - await self.__iterate_iterator_node(node) - - # now the output nodes outside of iterators - - # Now run everything that is not in an iterator lineage - non_iterator_output_nodes = [ - node - for node, iter_node in self.chain.get_parent_iterator_map().items() - if iter_node is None and node.has_side_effects() - ] - for output_node in non_iterator_output_nodes: - await self.progress.suspend() - await self.process_regular_node(output_node) - - # clear cache after the chain is done - self.cache.clear() - - # await all broadcasts - tasks = self.__broadcast_tasks - self.__broadcast_tasks = [] - for task in tasks: - await task - - async def run(self): - logger.debug(f"Running executor {self.id}") - try: - await self.__process_nodes() - finally: - gc.collect() - - def resume(self): - logger.debug(f"Resuming executor {self.id}") - self.progress.resume() - - def pause(self): - logger.debug(f"Pausing executor {self.id}") - self.progress.pause() - gc.collect() - - def kill(self): - logger.debug(f"Killing executor {self.id}") - self.progress.abort() - - # events - - async def __send_chain_start(self): - # all nodes except the cached ones - nodes = set(self.chain.nodes.keys()) - nodes.difference_update(self.cache.keys()) - - await self.queue.put( - { - "event": "chain-start", - "data": { - "nodes": list(nodes), - }, - } - ) - - async def __send_node_start(self, node: Node): - await self.queue.put( - { - "event": "node-start", - "data": { - "nodeId": node.id, - }, - } - ) - - async def __send_node_progress( - self, node: Node, times: Sequence[float], index: int, length: int - ): - def get_eta(times: Sequence[float]) -> float: - avg_time = 0 - if len(times) > 0: - # only consider the last 100 - times = times[-100:] - - # use a weighted average - weights = [max(1 / i, 0.9**i) for i in range(len(times), 0, -1)] - avg_time = sum(t * w for t, w in zip(times, weights)) / sum(weights) - - remaining = max(0, length - index) - return avg_time * remaining - - await self.queue.put( - { - "event": "node-progress", - "data": { - "nodeId": node.id, - "progress": 1 if length == 0 else index / length, - "index": index, - "total": length, - "eta": get_eta(times), - }, - } - ) - - async def __send_node_progress_done(self, node: Node, length: int): - await self.queue.put( - { - "event": "node-progress", - "data": { - "nodeId": node.id, - "progress": 1, - "index": length, - "total": length, - "eta": 0, - }, - } - ) - - async def __send_node_broadcast( - self, - node: Node, - output: Output, - ): - def compute_broadcast_data(): - if self.progress.aborted: - # abort the broadcast if the chain was aborted - return None - return compute_broadcast(output, node.data.outputs) - - async def send_broadcast(): - # TODO: Add the time it takes to compute the broadcast data to the execution time - result = await self.loop.run_in_executor(self.pool, compute_broadcast_data) - if result is None or self.progress.aborted: - return - - data, types = result - await self.queue.put( - { - "event": "node-broadcast", - "data": { - "nodeId": node.id, - "data": data, - "types": types, - }, - } - ) - - # Only broadcast the output if the node has outputs - if self.send_broadcast_data and len(node.data.outputs) > 0: - # broadcasts are done is parallel, so don't wait - self.__broadcast_tasks.append(self.loop.create_task(send_broadcast())) - - async def __send_node_finish( - self, - node: Node, - execution_time: float, - ): - await self.queue.put( - { - "event": "node-finish", - "data": { - "nodeId": node.id, - "executionTime": execution_time, - }, - } - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/response.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/response.py deleted file mode 100644 index 3d408cd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/response.py +++ /dev/null @@ -1,57 +0,0 @@ -from __future__ import annotations - -from typing import Literal, TypedDict - -from events import ExecutionErrorSource -from process import NodeExecutionError - - -class SuccessResponse(TypedDict): - type: Literal["success"] - - -class ErrorResponse(TypedDict): - type: Literal["error"] - message: str - exception: str - source: ExecutionErrorSource | None - - -class NoExecutorResponse(TypedDict): - type: Literal["no-executor"] - - -class AlreadyRunningResponse(TypedDict): - type: Literal["already-running"] - message: str - - -def success_response() -> SuccessResponse: - return {"type": "success"} - - -def error_response( - message: str, - exception: str | Exception, - source: ExecutionErrorSource | None = None, -) -> ErrorResponse: - if source is None and isinstance(exception, NodeExecutionError): - source = { - "nodeId": exception.node_id, - "schemaId": exception.node_data.schema_id, - "inputs": exception.inputs, - } - return { - "type": "error", - "message": message, - "exception": str(exception), - "source": source, - } - - -def no_executor_response() -> NoExecutorResponse: - return {"type": "no-executor"} - - -def already_running_response(message: str) -> AlreadyRunningResponse: - return {"type": "already-running", "message": message} diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/run.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/run.py deleted file mode 100644 index 86c1a78..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/run.py +++ /dev/null @@ -1,8 +0,0 @@ -import importlib - -# Install server dependencies. Can't start the server without them, but we don't want to install the other deps yet. -importlib.import_module("dependencies.install_server_deps") - -# Start the host server -server_host = importlib.import_module("server_host") -server_host.main() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/server.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/server.py deleted file mode 100644 index 4619e5b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/server.py +++ /dev/null @@ -1,575 +0,0 @@ -from __future__ import annotations - -import asyncio -import gc -import importlib -import logging -import sys -import tempfile -import traceback -import uuid -from concurrent.futures import ThreadPoolExecutor -from functools import cached_property -from json import dumps as stringify -from pathlib import Path -from typing import Final, TypedDict - -from sanic import Sanic -from sanic.log import access_logger, logger -from sanic.request import Request -from sanic.response import json -from sanic_cors import CORS - -import api -from api import ( - ExecutionOptions, - Group, - JsonExecutionOptions, - NodeId, -) -from chain.cache import OutputCache -from chain.chain import Chain, FunctionNode -from chain.json import JsonNode, parse_json -from chain.optimize import optimize -from dependencies.store import installed_packages -from events import EventConsumer, EventQueue, ExecutionErrorData -from process import ExecutionId, Executor, NodeExecutionError, NodeOutput -from progress_controller import Aborted -from response import ( - already_running_response, - error_response, - no_executor_response, - success_response, -) -from server_config import ServerConfig - - -class AppContext: - def __init__(self): - self.config: Final[ServerConfig] = ServerConfig.parse_argv() - self.executor: Executor | None = None - self.individual_executors: dict[ExecutionId, Executor] = {} - self.cache: dict[NodeId, NodeOutput] = {} - self.pool: Final[ThreadPoolExecutor] = ThreadPoolExecutor(max_workers=4) - - @cached_property - def queue(self) -> EventQueue: - return EventQueue() - - @cached_property - def storage_dir(self) -> Path: - if self.config.storage_dir is not None: - logger.info(f"Using given storage directory: {self.config.storage_dir}") - return Path(self.config.storage_dir) - - default_sub_dir = "chaiNNer/backend-storage" - - # try using the chaiNNer's app dir - try: - # appdirs is likely only installed on dev machines - from appdirs import user_data_dir - - app_data_dir = Path(user_data_dir(roaming=True)) / default_sub_dir - logger.info(f"Using app data as storage directory: {app_data_dir}") - return app_data_dir - except: # noqa: E722 - # ignore errors - pass - - # last resort: use the system's temporary directory - temp = Path(tempfile.gettempdir()) / default_sub_dir - logger.info(f"No storage directory given. Using temporary directory: {temp}") - return Path(temp) - - @staticmethod - def get(app_instance: Sanic) -> AppContext: - assert isinstance(app_instance.ctx, AppContext) - return app_instance.ctx - - -app = Sanic("chaiNNer_executor", ctx=AppContext()) -app.config.REQUEST_TIMEOUT = sys.maxsize -app.config.RESPONSE_TIMEOUT = sys.maxsize -CORS(app) - - -class SSEFilter(logging.Filter): - def filter(self, record): # noqa: ANN001 - request = record.request # type: ignore - return not ( - (request.endswith("/sse")) and record.status == 200 # type: ignore - ) - - -class ZeroCounter: - def __init__(self) -> None: - self.count = 0 - - async def wait_zero(self) -> None: - while self.count != 0: - await asyncio.sleep(0.01) - - def __enter__(self): - self.count += 1 - - def __exit__(self, _exc_type: object, _exc_value: object, _exc_traceback: object): - self.count -= 1 - - -run_individual_counter = ZeroCounter() - -setup_task = None - - -async def nodes_available(): - if setup_task is not None: - await setup_task - - -access_logger.addFilter(SSEFilter()) - - -@app.route("/nodes") -async def nodes(_request: Request): - """Gets a list of all nodes as well as the node information""" - await nodes_available() - logger.debug(api.registry.categories) - - node_list = [] - for node, sub in api.registry.nodes.values(): - node_dict = { - "schemaId": node.schema_id, - "name": node.name, - "category": sub.category.id, - "nodeGroup": sub.id, - "inputs": [x.to_dict() for x in node.inputs], - "outputs": [x.to_dict() for x in node.outputs], - "groupLayout": [ - g.to_dict() if isinstance(g, Group) else g for g in node.group_layout - ], - "iteratorInputs": [x.to_dict() for x in node.iterator_inputs], - "iteratorOutputs": [x.to_dict() for x in node.iterator_outputs], - "keyInfo": node.key_info.to_dict() if node.key_info else None, - "description": node.description, - "seeAlso": node.see_also, - "icon": node.icon, - "kind": node.kind, - "hasSideEffects": node.side_effects, - "deprecated": node.deprecated, - "features": node.features, - } - node_list.append(node_dict) - - return json( - { - "nodes": node_list, - "categories": [x.to_dict() for x in api.registry.categories], - "categoriesMissingNodes": [], - } - ) - - -class RunRequest(TypedDict): - data: list[JsonNode] - options: JsonExecutionOptions - sendBroadcastData: bool - - -@app.route("/run", methods=["POST"]) -async def run(request: Request): - """Runs the provided nodes""" - await nodes_available() - ctx = AppContext.get(request.app) - - if ctx.executor: - message = "Cannot run another executor while the first one is still running." - logger.warning(message) - return json(already_running_response(message), status=500) - - try: - # wait until all previews are done - await run_individual_counter.wait_zero() - - full_data: RunRequest = dict(request.json) # type: ignore - logger.debug(full_data) - chain = parse_json(full_data["data"]) - optimize(chain) - - logger.info("Running new executor...") - executor = Executor( - id=ExecutionId("main-executor " + uuid.uuid4().hex), - chain=chain, - send_broadcast_data=full_data["sendBroadcastData"], - options=ExecutionOptions.parse(full_data["options"]), - loop=app.loop, - queue=ctx.queue, - pool=ctx.pool, - storage_dir=ctx.storage_dir, - parent_cache=OutputCache(static_data=ctx.cache.copy()), - ) - try: - ctx.executor = executor - await executor.run() - except Aborted: - pass - finally: - ctx.executor = None - gc.collect() - - return json(success_response(), status=200) - except Exception as exception: - logger.error(exception) - if ( - isinstance(exception, NodeExecutionError) - and exception.__cause__ is not None - ): - trace = "".join( - traceback.format_exception( - type(exception.__cause__), - exception.__cause__, - exception.__cause__.__traceback__, - ) - ) - else: - trace = traceback.format_exc() - logger.error(trace) - - error: ExecutionErrorData = { - "message": "Error running nodes!", - "source": None, - "exception": str(exception), - "exceptionTrace": trace, - } - if isinstance(exception, NodeExecutionError): - error["source"] = { - "nodeId": exception.node_id, - "schemaId": exception.node_data.schema_id, - "inputs": exception.inputs, - } - - await ctx.queue.put({"event": "execution-error", "data": error}) - return json(error_response("Error running nodes!", exception), status=500) - - -class RunIndividualRequest(TypedDict): - id: NodeId - inputs: list[object] - schemaId: str - options: JsonExecutionOptions - - -@app.route("/run/individual", methods=["POST"]) -async def run_individual(request: Request): - """Runs a single node""" - await nodes_available() - ctx = AppContext.get(request.app) - try: - full_data: RunIndividualRequest = dict(request.json) # type: ignore - logger.debug(full_data) - - node_id = full_data["id"] - ctx.cache.pop(node_id, None) - - node = FunctionNode(node_id, full_data["schemaId"]) - chain = Chain() - chain.add_node(node) - - for index, i in enumerate(full_data["inputs"]): - chain.inputs.set(node_id, node.data.inputs[index].id, i) - - # only yield certain types of events - queue = EventConsumer.filter( - ctx.queue, {"node-finish", "node-broadcast", "execution-error"} - ) - - execution_id = ExecutionId("individual-executor " + node_id) - executor = Executor( - id=execution_id, - chain=chain, - send_broadcast_data=True, - options=ExecutionOptions.parse(full_data["options"]), - loop=app.loop, - queue=queue, - storage_dir=ctx.storage_dir, - pool=ctx.pool, - ) - - with run_individual_counter: - try: - if execution_id in ctx.individual_executors: - # kill the previous executor (if any) - old_executor = ctx.individual_executors[execution_id] - old_executor.kill() - - ctx.individual_executors[execution_id] = executor - output = await executor.process_regular_node(node) - ctx.cache[node_id] = output - except Aborted: - pass - finally: - if ctx.individual_executors.get(execution_id, None) == executor: - ctx.individual_executors.pop(execution_id, None) - gc.collect() - - return json({"success": True, "data": None}) - except Exception as exception: - logger.error(exception, exc_info=True) - return json({"success": False, "error": str(exception)}) - - -@app.route("/clear-cache/individual", methods=["POST"]) -async def clear_cache_individual(request: Request): - await nodes_available() - ctx = AppContext.get(request.app) - try: - full_data = dict(request.json) # type: ignore - if ctx.cache.get(full_data["id"], None) is not None: - del ctx.cache[full_data["id"]] - return json({"success": True, "data": None}) - except Exception as exception: - logger.error(exception, exc_info=True) - return json({"success": False, "error": str(exception)}) - - -@app.route("/pause", methods=["POST"]) -async def pause(request: Request): - """Pauses the current execution""" - await nodes_available() - ctx = AppContext.get(request.app) - - logger.info("Attempting to pause executor...") - - if not ctx.executor: - logger.warning("No executor to pause.") - return json(no_executor_response(), status=400) - - try: - ctx.executor.pause() - logger.info("Paused executor.") - return json(success_response(), status=200) - except Exception as exception: - logger.log(2, exception, exc_info=True) - return json(error_response("Error pausing execution!", exception), status=500) - - -@app.route("/resume", methods=["POST"]) -async def resume(request: Request): - """Pauses the current execution""" - await nodes_available() - ctx = AppContext.get(request.app) - - logger.info("Attempting to resume executor...") - - if not ctx.executor: - logger.warning("No executor to resume.") - return json(no_executor_response(), status=400) - - try: - ctx.executor.resume() - logger.info("Resumed executor.") - return json(success_response(), status=200) - except Exception as exception: - logger.log(2, exception, exc_info=True) - return json(error_response("Error resuming execution!", exception), status=500) - - -@app.route("/kill", methods=["POST"]) -async def kill(request: Request): - """Kills the current execution""" - await nodes_available() - ctx = AppContext.get(request.app) - - logger.info("Attempting to kill executor...") - - if not ctx.executor: - logger.warning("No executor to kill.") - return json(no_executor_response(), status=400) - - try: - ctx.executor.kill() - while ctx.executor: - await asyncio.sleep(0.0001) - logger.info("Killed executor.") - return json(success_response(), status=200) - except Exception as exception: - logger.log(2, exception, exc_info=True) - return json(error_response("Error killing execution!", exception), status=500) - - -@app.route("/packages", methods=["GET"]) -async def get_packages(request: Request): - await nodes_available() - - hide_internal = request.args.get("hideInternal", "true") == "true" - - packages = [] - for package in api.registry.packages.values(): - if package.name == "chaiNNer_standard" and hide_internal: - continue - - pkg_dependencies = [] - for pkg_dep in package.dependencies: - installed_version = installed_packages.get(pkg_dep.pypi_name, None) - pkg_dep_item = { - **pkg_dep.to_dict(), - } - if installed_version is None: - pkg_dep_item["installed"] = None - else: - pkg_dep_item["installed"] = installed_version - pkg_dependencies.append(pkg_dep_item) - - packages.append(package.to_dict()) - - return json(packages) - - -@app.route("/features") -async def get_features(_request: Request): - await nodes_available() - - features: list[tuple[api.Feature, api.Package]] = [] - for package in api.registry.packages.values(): - for feature in package.features: - features.append((feature, package)) - - # check all features in parallel - async def check(feature: api.Feature) -> api.FeatureState | None: - if feature.behavior is None: - # no behavior assigned - return None - - try: - return await feature.behavior.check() - except Exception as e: - return api.FeatureState.disabled(str(e)) - - # because good API design just isn't pythonic, asyncio.gather will return List[Any]. - results: list[api.FeatureState | None] = await asyncio.gather( - *[check(f) for f, _ in features] - ) - - features_json = [] - for (feature, package), state in zip(features, results): - if state is None: - continue - - features_json.append( - { - "packageId": package.id, - "featureId": feature.id, - "enabled": state.is_enabled, - "details": state.details, - } - ) - - return json(features_json) - - -@app.get("/sse") -async def sse(request: Request): - ctx = AppContext.get(request.app) - headers = {"Cache-Control": "no-cache"} - response = await request.respond(headers=headers, content_type="text/event-stream") - if response is None: - return - - while True: - message = await ctx.queue.get() - await response.send( - f"event: {message['event']}\n" f"data: {stringify(message['data'])}\n\n" - ) - - -async def import_packages( - config: ServerConfig, -): - importlib.import_module("packages.chaiNNer_standard") - importlib.import_module("packages.chaiNNer_pytorch") - importlib.import_module("packages.chaiNNer_ncnn") - importlib.import_module("packages.chaiNNer_onnx") - importlib.import_module("packages.chaiNNer_external") - - logger.info("Loading Nodes...") - - load_errors = api.registry.load_nodes(__file__) - if len(load_errors) > 0: - import_errors: list[api.LoadErrorInfo] = [] - for e in load_errors: - if not isinstance(e.error, ModuleNotFoundError): - logger.warning( - f"Failed to load {e.module} ({e.file}):", exc_info=e.error - ) - else: - import_errors.append(e) - - if len(import_errors) > 0: - logger.warning(f"Failed to import {len(import_errors)} modules:") - - by_error: dict[str, list[api.LoadErrorInfo]] = {} - for e in import_errors: - key = str(e.error) - if key not in by_error: - by_error[key] = [] - by_error[key].append(e) - - for error in sorted(by_error.keys()): - modules = [e.module for e in by_error[error]] - if len(modules) == 1: - logger.warning(f"{error} -> {modules[0]}") - else: - count = len(modules) - if count > 3: - modules = modules[:2] + [f"and {count - 2} more ..."] - l = "\n".join(" -> " + m for m in modules) - logger.warning(f"{error} -> {count} modules ...\n{l}") - - if config.error_on_failed_node: - raise ValueError("Error importing nodes") - - -async def setup(sanic_app: Sanic): - await import_packages(AppContext.get(sanic_app).config) - - -exit_code = 0 - - -async def close_server(sanic_app: Sanic): - # pylint: disable=global-statement - global exit_code - - try: - await nodes_available() - except Exception as ex: - logger.error(f"Error waiting for server to start: {ex}") - exit_code = 1 - - # now we can close the server - logger.info("Closing server...") - sanic_app.stop() - - -@app.after_server_start -async def after_server_start(sanic_app: Sanic, loop: asyncio.AbstractEventLoop): - # pylint: disable=global-statement - global setup_task - ctx = AppContext.get(sanic_app) - - # start the setup task - setup_task = loop.create_task(setup(sanic_app)) - - # start task to close the server - if ctx.config.close_after_start: - loop.create_task(close_server(sanic_app)) - - -def main(): - config = AppContext.get(app).config - app.run(port=config.port, single_process=True) - if exit_code != 0: - sys.exit(exit_code) - - -if __name__ == "__main__": - main() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/server_config.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/server_config.py deleted file mode 100644 index d51b5b7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/server_config.py +++ /dev/null @@ -1,81 +0,0 @@ -from __future__ import annotations - -import argparse -from dataclasses import dataclass - - -@dataclass -class ServerConfig: - port: int - """Port to run the server on.""" - - close_after_start: bool - """ - Whether to close the server after starting it. - - This is useful for testing the server. - - Usage: `--close-after-start` - """ - - install_builtin_packages: bool - """ - Whether to install all built-in packages. - - Usage: `--install-builtin-packages` - """ - - error_on_failed_node: bool - """ - Errors and exits the server with a non-zero exit code if a node fails to import. - - Usage: `--error-on-failed-node` - """ - - storage_dir: str | None - """ - Directory to store for nodes to store files in. - - Usage: `--storage-dir /foo/bar` - """ - - @staticmethod - def parse_argv() -> ServerConfig: - parser = argparse.ArgumentParser(description="ChaiNNer's server.") - parser.add_argument( - "port", - type=int, - nargs="?", - default=8000, - help="Port to run the server on.", - ) - parser.add_argument( - "--close-after-start", - action="store_true", - help="Close the server after starting Useful for CI.", - ) - parser.add_argument( - "--install-builtin-packages", - action="store_true", - help="Install all built-in packages.", - ) - parser.add_argument( - "--error-on-failed-node", - action="store_true", - help="Errors and exits the server with a non-zero exit code if a node fails to import.", - ) - parser.add_argument( - "--storage-dir", - type=str, - help="Directory to store for nodes to store files in.", - ) - - parsed = parser.parse_args() - - return ServerConfig( - port=parsed.port, - close_after_start=parsed.close_after_start, - install_builtin_packages=parsed.install_builtin_packages, - error_on_failed_node=parsed.error_on_failed_node, - storage_dir=parsed.storage_dir or None, - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/server_host.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/server_host.py deleted file mode 100644 index a93e45a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/server_host.py +++ /dev/null @@ -1,501 +0,0 @@ -from __future__ import annotations - -import asyncio -import logging -import sys -from concurrent.futures import ThreadPoolExecutor -from dataclasses import asdict, dataclass -from functools import cached_property -from json import dumps as stringify -from typing import Final - -import psutil -from sanic import Sanic -from sanic.log import access_logger, logger -from sanic.request import Request -from sanic.response import json -from sanic_cors import CORS - -import api -from custom_types import UpdateProgressFn -from dependencies.store import ( - DependencyInfo, - filter_necessary_to_install, - install_dependencies, - installed_packages, - uninstall_dependencies, -) -from events import EventQueue -from gpu import nvidia -from response import error_response, success_response -from server_config import ServerConfig -from server_process_helper import WorkerServer - - -class AppContext: - def __init__(self): - self.config: Final[ServerConfig] = ServerConfig.parse_argv() - - # flags to pass along to the worker - worker_flags: list[str] = [] - if self.config.storage_dir is not None: - worker_flags.extend(["--storage-dir", self.config.storage_dir]) - - self._worker: Final[WorkerServer] = WorkerServer(worker_flags) - self.pool: Final[ThreadPoolExecutor] = ThreadPoolExecutor(max_workers=4) - self.is_ready = False - - def get_worker_unmanaged(self) -> WorkerServer: - """ - Returns the worker server instance no matter what state it is currently in. - """ - return self._worker - - async def get_worker(self) -> WorkerServer: - """ - Returns the worker server instance after it is ready. - """ - while not self.is_ready: - await asyncio.sleep(0.1) - return self._worker - - @cached_property - def setup_queue(self) -> EventQueue: - return EventQueue() - - @staticmethod - def get(app_instance: Sanic) -> AppContext: - assert isinstance(app_instance.ctx, AppContext) - return app_instance.ctx - - -app = Sanic("chaiNNer_host", ctx=AppContext()) -app.config.REQUEST_TIMEOUT = sys.maxsize -app.config.RESPONSE_TIMEOUT = sys.maxsize -CORS(app) - - -class SSEFilter(logging.Filter): - def filter(self, record): # noqa: ANN001 - request = record.request # type: ignore - return not ( - (request.endswith(("/sse", "/setup-sse"))) and record.status == 200 # type: ignore - ) - - -access_logger.addFilter(SSEFilter()) - - -@app.route("/nodes") -async def nodes(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request) - - -@app.route("/run", methods=["POST"]) -async def run(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request, timeout=None) - - -@app.route("/run/individual", methods=["POST"]) -async def run_individual(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request) - - -@app.route("/clear-cache/individual", methods=["POST"]) -async def clear_cache_individual(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request) - - -@app.route("/pause", methods=["POST"]) -async def pause(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request) - - -@app.route("/resume", methods=["POST"]) -async def resume(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request, timeout=None) - - -@app.route("/kill", methods=["POST"]) -async def kill(request: Request): - worker = await AppContext.get(request.app).get_worker() - try: - response = await worker.proxy_request(request, timeout=3) - if response.status > 200: - if response.body is None: - raise Exception( - "Unknown error occurred while attempting to stop execution." - ) - raise Exception(response.body.decode()) - except Exception: - try: - logger.debug( - "Regular kill failed, attempting to restart executor process..." - ) - await worker.restart() - except Exception as exception: - return json( - error_response("Error killing execution!", exception), status=500 - ) - return json(success_response(), status=200) - - -@app.route("/python-info", methods=["GET"]) -async def python_info(_request: Request): - version = ( - f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" - ) - return json({"python": sys.executable, "version": version}) - - -@dataclass -class SystemStat: - label: str - percent: float - - -@app.route("/system-usage", methods=["GET"]) -async def system_usage(_request: Request): - stats_list = [] - cpu_usage = psutil.cpu_percent() - mem_usage = psutil.virtual_memory().percent - stats_list.append(SystemStat("CPU", cpu_usage)) - stats_list.append(SystemStat("RAM", mem_usage)) - for device in nvidia.devices: - usage = device.get_current_vram_usage() - stats_list.append( - SystemStat( - f"VRAM {device.index}" if len(nvidia.devices) > 1 else "VRAM", - usage.used / usage.total * 100, - ) - ) - return json([asdict(x) for x in stats_list]) - - -@app.route("/packages", methods=["GET"]) -async def get_packages(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request) - - -@app.route("/installed-dependencies", methods=["GET"]) -async def get_installed_dependencies(request: Request): - worker = await AppContext.get(request.app).get_worker() - installed_deps: dict[str, str] = {} - packages = await worker.get_packages() - for package in packages: - for pkg_dep in package.dependencies: - installed_version = installed_packages.get(pkg_dep.pypi_name, None) - if installed_version is not None: - installed_deps[pkg_dep.pypi_name] = installed_version - - return json(installed_deps) - - -@app.route("/features") -async def get_features(request: Request): - worker = await AppContext.get(request.app).get_worker() - return await worker.proxy_request(request) - - -def deps_to_dep_info(deps: list[api.Dependency]) -> list[DependencyInfo]: - return [ - DependencyInfo( - package_name=dep.pypi_name, - display_name=dep.display_name, - version=dep.version, - extra_index_url=dep.extra_index_url, - ) - for dep in deps - ] - - -@app.route("/packages/uninstall", methods=["POST"]) -async def uninstall_dependencies_request(request: Request): - full_data = dict(request.json) # type: ignore - package_to_uninstall = full_data["package"] - - worker = await AppContext.get(request.app).get_worker() - packages = await worker.get_packages() - - package = next((x for x in packages if x.id == package_to_uninstall), None) - - if package is None: - return json( - {"status": "error", "message": f"Package {package_to_uninstall} not found"}, - status=404, - ) - - def update_progress( - message: str, progress: float, status_progress: float | None = None - ): - return AppContext.get(request.app).setup_queue.put_and_wait( - { - "event": "package-install-status", - "data": { - "message": message, - "progress": progress, - "statusProgress": status_progress, - }, - }, - timeout=0.01, - ) - - try: - await worker.stop() - try: - await uninstall_dependencies( - deps_to_dep_info(package.dependencies), update_progress, logger - ) - finally: - await worker.start() - return json({"status": "ok"}) - except Exception as ex: - logger.error(f"Error uninstalling dependencies: {ex}", exc_info=True) - return json({"status": "error", "message": str(ex)}, status=500) - - -@app.route("/packages/install", methods=["POST"]) -async def install_dependencies_request(request: Request): - full_data = dict(request.json) # type: ignore - package_to_install = full_data["package"] - - def update_progress( - message: str, progress: float, status_progress: float | None = None - ): - logger.info(f"Progress: {message} {progress} {status_progress}") - return AppContext.get(request.app).setup_queue.put_and_wait( - { - "event": "package-install-status", - "data": { - "message": message, - "progress": progress, - "statusProgress": status_progress, - }, - }, - timeout=0.01, - ) - - worker = await AppContext.get(request.app).get_worker() - packages = await worker.get_packages() - package = next((x for x in packages if x.id == package_to_install), None) - - if package is None: - return json( - {"status": "error", "message": f"Package {package_to_install} not found"}, - status=404, - ) - - try: - await worker.stop() - await install_dependencies( - deps_to_dep_info(package.dependencies), update_progress, logger - ) - await worker.start() - return json({"status": "ok"}) - except Exception as ex: - logger.error(f"Error installing dependencies: {ex}", exc_info=True) - return json({"status": "error", "message": str(ex)}, status=500) - - -@app.get("/sse") -async def sse(request: Request): - headers = {"Cache-Control": "no-cache"} - response = await request.respond(headers=headers, content_type="text/event-stream") - if response is None: - return - - worker = await AppContext.get(request.app).get_worker() - while True: - try: - async for data in worker.get_sse(request): - await response.send(data) - except Exception: - break - - -@app.get("/setup-sse") -async def setup_sse(request: Request): - ctx = AppContext.get(request.app) - headers = {"Cache-Control": "no-cache"} - response = await request.respond(headers=headers, content_type="text/event-stream") - if response is None: - return - - while True: - try: - message = await ctx.setup_queue.get() - await response.send( - f"event: {message['event']}\n" f"data: {stringify(message['data'])}\n\n" - ) - except Exception: - break - - -@app.post("/shutdown") -async def shutdown(request: Request): - await close_server(request.app) - return json(success_response()) - - -@app.get("/status") -async def status(request: Request): - ctx = AppContext.get(request.app) - return json({"ready": ctx.is_ready}) - - -async def import_packages( - cxt: AppContext, - update_progress_cb: UpdateProgressFn, -): - config = cxt.config - worker = cxt.get_worker_unmanaged() - packages = await worker.get_packages() - - logger.info("Checking dependencies...") - - to_install: list[api.Dependency] = [] - for package in packages: - logger.info(f"Checking dependencies for {package.name}...") - - if config.install_builtin_packages: - to_install.extend(package.dependencies) - continue - - if package.name == "chaiNNer_standard": - to_install.extend(package.dependencies) - - # check auto updates - for dep in package.dependencies: - is_installed = installed_packages.get(dep.pypi_name, None) is not None - if dep.auto_update and is_installed: - to_install.append(dep) - - try: - deps_to_install = filter_necessary_to_install(deps_to_dep_info(to_install)) - - restart_flags: list[str] = [] - if config.error_on_failed_node: - restart_flags.append("--error-on-failed-node") - if config.close_after_start: - restart_flags.append("--close-after-start") - - if len(restart_flags) > 0 or len(deps_to_install) > 0: - await worker.stop() - await install_dependencies(deps_to_install, update_progress_cb, logger) - await worker.start(restart_flags) - else: - logger.info("No dependencies to install. Skipping worker restart.") - except Exception as ex: - logger.error(f"Error installing dependencies: {ex}", exc_info=True) - if config.close_after_start: - raise ValueError("Error installing dependencies") from ex - - logger.info("Done checking dependencies...") - - -async def setup(sanic_app: Sanic, loop: asyncio.AbstractEventLoop): - ctx = AppContext.get(sanic_app) - worker = ctx.get_worker_unmanaged() - setup_queue = ctx.setup_queue - - async def update_progress( - message: str, progress: float, status_progress: float | None = None - ): - await setup_queue.put_and_wait( - { - "event": "backend-status", - "data": { - "message": message, - "progress": progress, - "statusProgress": status_progress, - }, - }, - timeout=1, - ) - - logger.info("Starting setup...") - - await setup_queue.put_and_wait( - { - "event": "backend-started", - "data": None, - }, - timeout=1, - ) - - await update_progress("Importing nodes...", 0.0, None) - - logger.info("Importing nodes...") - - # Now we can load all the nodes - await import_packages(ctx, update_progress) - - logger.info("Backend almost ready...") - - await update_progress("Loading Nodes...", 1.0, None) - - # Wait to set backend-ready until nodes are loaded - await worker.wait_for_ready() - ctx.is_ready = True - - logger.info("Done.") - - -setup_task = None - - -async def close_server(sanic_app: Sanic): - # now we can close the server - logger.info("Closing server...") - - try: - if setup_task is not None: - await setup_task - except Exception as ex: - logger.error(f"Error waiting for server to start: {ex}") - - worker = AppContext.get(sanic_app).get_worker_unmanaged() - await worker.stop() - sanic_app.stop() - - -@app.after_server_stop -async def after_server_stop(sanic_app: Sanic, _loop: asyncio.AbstractEventLoop): - worker = AppContext.get(sanic_app).get_worker_unmanaged() - await worker.stop() - logger.info("Server closed.") - - -@app.after_server_start -async def after_server_start(sanic_app: Sanic, loop: asyncio.AbstractEventLoop): - global setup_task - - # initialize the queues - ctx = AppContext.get(sanic_app) - - worker = ctx.get_worker_unmanaged() - await worker.start() - await worker.wait_for_ready() - - # start the setup task - setup_task = loop.create_task(setup(sanic_app, loop)) - - # start task to close the server - if ctx.config.close_after_start: - loop.create_task(close_server(sanic_app)) - - -def main(): - config = AppContext.get(app).config - app.run(port=config.port, single_process=True) - - -if __name__ == "__main__": - main() diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/server_process_helper.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/server_process_helper.py deleted file mode 100644 index 42755d8..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/server_process_helper.py +++ /dev/null @@ -1,274 +0,0 @@ -from __future__ import annotations - -import asyncio -import atexit -import os -import re -import socket -import subprocess -import sys -import threading -import time -from typing import Iterable - -import aiohttp -from sanic import HTTPResponse, Request -from sanic.log import logger - -from api import Package - - -def _find_free_port(): - with socket.socket() as s: - s.bind(("", 0)) # Bind to a free port provided by the host. - return s.getsockname()[1] # Return the port number assigned. - - -def _port_in_use(port: int): - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - return s.connect_ex(("127.0.0.1", port)) == 0 - - -SANIC_LOG_REGEX = re.compile(r"^\s*\[[^\[\]]*\] \[\d*\] \[(\w*)\] (.*)") - -ENV = {**os.environ, "PYTHONIOENCODING": "utf-8"} - - -class _WorkerProcess: - def __init__(self, flags: list[str]): - server_file = os.path.join(os.path.dirname(__file__), "server.py") - python_location = sys.executable - - self._process = subprocess.Popen( - [python_location, server_file, *flags], - shell=False, - stdin=None, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - encoding="utf-8", - env=ENV, - ) - self._stop_event = threading.Event() - - # Create a separate thread to read and print the output of the subprocess - self._stdout_thread = threading.Thread( - target=self._read_stdout, - daemon=True, - name="stdout reader", - ) - self._stdout_thread.start() - self._stderr_thread = threading.Thread( - target=self._read_stderr, - daemon=True, - name="stderr reader", - ) - self._stderr_thread.start() - - atexit.register(self.close) - - def close(self): - if self._process is None: - # already closed - return - - logger.info("Closing worker process...") - self._stop_event.set() - try: - self._process.terminate() - self._process.kill() - except Exception: - logger.error("Failed to terminate worker process", exc_info=True) - self._process = None - atexit.unregister(self.close) - - self._stdout_thread = None - self._stderr_thread = None - - def _read_stdout(self): - p = self._process - if p is None or p.stdout is None: - return - - stopped = False - for line in p.stdout: - stopped = self._stop_event.is_set() - if stopped: - break - - stripped_line = line.rstrip() - match_obj = re.match(SANIC_LOG_REGEX, stripped_line) - if match_obj is not None: - log_level, message = match_obj.groups() - message = f"[Worker] {message}" - if log_level == "DEBUG": - logger.debug(message) - elif log_level == "INFO": - logger.info(message) - elif log_level == "WARNING": - logger.warning(message) - elif log_level == "ERROR": - logger.error(message) - elif log_level == "CRITICAL": - logger.critical(message) - else: - logger.info(message) - else: - logger.info(f"[Worker] {stripped_line}") - - cause = "stop event" if stopped else "stdout ending" - logger.info(f"Stopped reading worker stdout due to {cause}") - - stopped = self._stop_event.is_set() - if not stopped: - # the worker ended on its own, so it likely crashed - returncode = p.wait() - if returncode == 0: - logger.info("Worker process ended normally") - else: - logger.error( - f"Worker process ended with non-zero return code {returncode}" - ) - - def _read_stderr(self): - p = self._process - if p is None or p.stderr is None: - return - - stopped = False - for line in p.stderr: - stopped = self._stop_event.is_set() - if stopped: - break - - stripped_line = line.rstrip() - logger.error(f"[Worker] {stripped_line}") - - cause = "stop event" if stopped else "stderr ending" - logger.info(f"Stopped reading worker stderr due to {cause}") - - -class WorkerServer: - def __init__(self, flags: Iterable[str] = []): - self._process = None - - self._port = _find_free_port() - self._base_url = f"http://127.0.0.1:{self._port}" - self._flags = list(flags) - self._session = None - self._is_ready = False - self._is_checking_ready = False - self._manually_close: set[aiohttp.ClientResponse] = set() - - async def start(self, extra_flags: Iterable[str] = []): - logger.info(f"Starting worker process on port {self._port}...") - self._process = _WorkerProcess([str(self._port), *self._flags, *extra_flags]) - self._session = aiohttp.ClientSession(base_url=self._base_url) - self._is_ready = False - self._is_checking_ready = False - await self.wait_for_ready() - logger.info("Worker process started") - - async def stop(self): - if self._process: - self._process.close() - if self._session: - for resp in self._manually_close: - resp.close() - self._manually_close.clear() - await self._session.close() - logger.info("Worker process stopped") - - async def restart(self, extra_flags: Iterable[str] = []): - logger.info("Restarting worker...") - await self.stop() - await self.start(extra_flags) - - async def wait_for_ready(self, timeout: float = 300): - if self._is_ready: - return - - async def test_connection(session: aiohttp.ClientSession): - async with session.get("/nodes", timeout=5) as resp: - resp.raise_for_status() - - start = time.time() - while self._is_checking_ready and time.time() - start < timeout: - await asyncio.sleep(0.1) - - if self._is_ready: - return - - try: - self._is_checking_ready = True - - while time.time() - start < timeout: - if ( - self._process is not None - and self._session is not None - and _port_in_use(self._port) - ): - try: - if not self._is_ready: - await test_connection(self._session) - self._is_ready = True - return - except asyncio.TimeoutError: - logger.warn("Server not ready yet due to timeout") - except Exception as e: - logger.warn("Server not ready yet", exc_info=e) - - await asyncio.sleep(0.1) - - raise TimeoutError("Server did not start in time") - finally: - self._is_checking_ready = False - - async def proxy_request(self, request: Request, timeout: int | None = 300): - if request.route is None: - raise ValueError("Route not found") - await self.wait_for_ready() - assert self._session is not None - async with self._session.request( - request.method, - f"/{request.route.path}", - headers=request.headers, - data=request.body, - timeout=timeout, - ) as resp: - headers = resp.headers - status = resp.status - body = await resp.read() - return HTTPResponse( - body, - status=status, - headers=dict(headers), - content_type=request.content_type, - ) - - async def get_sse(self, request: Request): - await self.wait_for_ready() - assert self._session is not None - async with self._session.request( - request.method, - "/sse", - headers=request.headers, - data=request.body, - timeout=aiohttp.ClientTimeout(total=60 * 60, connect=5), - ) as resp: - self._manually_close.add(resp) - try: - async for data, _ in resp.content.iter_chunks(): - yield data - finally: - self._manually_close.remove(resp) - - async def get_packages(self): - await self.wait_for_ready() - assert self._session is not None - logger.debug("Fetching packages...") - async with self._session.get( - "/packages", params={"hideInternal": "false"} - ) as packages_resp: - packages_json = await packages_resp.json() - packages = [Package.from_dict(p) for p in packages_json] - return packages diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/settings.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/settings.py deleted file mode 100644 index 3a560bb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/settings.py +++ /dev/null @@ -1,18 +0,0 @@ -from dataclasses import dataclass -from typing import Any - -from api import SettingsParser -from nodes.utils.exec_options import get_execution_options - - -@dataclass(frozen=True) -class GeneralSettings: - example: bool - - -def get_global_settings() -> Any: - settings = SettingsParser(get_execution_options().get_package_settings("general")) - - return GeneralSettings( - example=settings.get_bool("example", default=False), - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/setup.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/setup.py deleted file mode 100644 index e4c983f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -from setuptools import setup -from Cython.Build import cythonize - -setup( - name='test run auto levels folder', - ext_modules=cythonize("./backend/src/testrunautolevelszipmulti.py"), -) diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/texconv/LICENSE b/MangaJaNaiConverterGui/chaiNNer/backend/src/texconv/LICENSE deleted file mode 100644 index eb3a123..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/texconv/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ - The MIT License (MIT) - -Copyright (c) 2011-2022 Microsoft Corp - -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/texconv/texconv.exe b/MangaJaNaiConverterGui/chaiNNer/backend/src/texconv/texconv.exe deleted file mode 100644 index f479a99..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/backend/src/texconv/texconv.exe and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/src/util.py b/MangaJaNaiConverterGui/chaiNNer/backend/src/util.py deleted file mode 100644 index c453db4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/src/util.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import annotations - -import time -from typing import Callable, TypeVar - -T = TypeVar("T") - - -def timed_supplier(supplier: Callable[[], T]) -> Callable[[], tuple[T, float]]: - def wrapper(): - start = time.time() - result = supplier() - duration = time.time() - start - return result, duration - - return wrapper diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/tests/__init__.py b/MangaJaNaiConverterGui/chaiNNer/backend/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/backend/tests/test_dummy.py b/MangaJaNaiConverterGui/chaiNNer/backend/tests/test_dummy.py deleted file mode 100644 index 10cf3ad..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/backend/tests/test_dummy.py +++ /dev/null @@ -1,2 +0,0 @@ -def test_dummy(): - pass diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_IllustrationJaNai_V1_ESRGAN_120k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_IllustrationJaNai_V1_ESRGAN_120k.pth deleted file mode 100644 index ba90dd4..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_IllustrationJaNai_V1_ESRGAN_120k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1200p_V1_ESRGAN_70k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1200p_V1_ESRGAN_70k.pth deleted file mode 100644 index f5a32be..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1200p_V1_ESRGAN_70k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1300p_V1_ESRGAN_75k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1300p_V1_ESRGAN_75k.pth deleted file mode 100644 index 59c539b..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1300p_V1_ESRGAN_75k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1400p_V1_ESRGAN_70k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1400p_V1_ESRGAN_70k.pth deleted file mode 100644 index 058293d..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1400p_V1_ESRGAN_70k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1500p_V1_ESRGAN_90k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1500p_V1_ESRGAN_90k.pth deleted file mode 100644 index 546052d..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1500p_V1_ESRGAN_90k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1600p_V1_ESRGAN_90k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1600p_V1_ESRGAN_90k.pth deleted file mode 100644 index d2fd5f0..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1600p_V1_ESRGAN_90k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1920p_V1_ESRGAN_70k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1920p_V1_ESRGAN_70k.pth deleted file mode 100644 index 98b919c..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_1920p_V1_ESRGAN_70k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_2048p_V1_ESRGAN_95k.pth b/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_2048p_V1_ESRGAN_95k.pth deleted file mode 100644 index c2a4333..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/2x_MangaJaNai_2048p_V1_ESRGAN_95k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_IllustrationJaNai_V1_ESRGAN_135k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_IllustrationJaNai_V1_ESRGAN_135k.pth deleted file mode 100644 index c177c14..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_IllustrationJaNai_V1_ESRGAN_135k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1200p_V1_ESRGAN_70k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1200p_V1_ESRGAN_70k.pth deleted file mode 100644 index 6dcbbc6..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1200p_V1_ESRGAN_70k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1300p_V1_ESRGAN_75k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1300p_V1_ESRGAN_75k.pth deleted file mode 100644 index dd951e0..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1300p_V1_ESRGAN_75k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1400p_V1_ESRGAN_105k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1400p_V1_ESRGAN_105k.pth deleted file mode 100644 index cc94873..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1400p_V1_ESRGAN_105k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1500p_V1_ESRGAN_105k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1500p_V1_ESRGAN_105k.pth deleted file mode 100644 index 9cf1e02..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1500p_V1_ESRGAN_105k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1600p_V1_ESRGAN_70k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1600p_V1_ESRGAN_70k.pth deleted file mode 100644 index ea9ce06..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1600p_V1_ESRGAN_70k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1920p_V1_ESRGAN_105k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1920p_V1_ESRGAN_105k.pth deleted file mode 100644 index 9569587..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_1920p_V1_ESRGAN_105k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_2048p_V1_ESRGAN_70k.pth b/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_2048p_V1_ESRGAN_70k.pth deleted file mode 100644 index cf69291..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/models/4x_MangaJaNai_2048p_V1_ESRGAN_70k.pth and /dev/null differ diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/INSTALLER b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/LICENSE b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/LICENSE deleted file mode 100644 index c270f2c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Original work Copyright (C) 2011-2018 by Hong Minhee -Modified work Copyright (C) 2019-2023 by E. McConville - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/METADATA b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/METADATA deleted file mode 100644 index 3bf97f6..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/METADATA +++ /dev/null @@ -1,128 +0,0 @@ -Metadata-Version: 2.1 -Name: Wand -Version: 0.6.11 -Summary: Ctypes-based simple MagickWand API binding for Python -Home-page: http://wand-py.org/ -Author: Hong Minhee -Author-email: hongminhee@member.fsf.org -Maintainer: E. McConville -Maintainer-email: emcconville@emcconville.com -License: MIT License -Project-URL: Documentation, https://docs.wand-py.org -Project-URL: Source, https://github.com/emcconville/wand -Project-URL: Tracker, https://github.com/emcconville/wand/issues -Keywords: ImageMagick ctypes -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.3 -Classifier: Programming Language :: Python :: 3.4 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Programming Language :: Python :: Implementation :: Stackless -Classifier: Topic :: Multimedia :: Graphics -Description-Content-Type: text/x-rst -License-File: LICENSE -Provides-Extra: doc -Requires-Dist: Sphinx (>=5.3.0) ; extra == 'doc' -Provides-Extra: test -Requires-Dist: pytest (>=7.2.0) ; extra == 'test' - -.. image:: https://docs.wand-py.org/en/latest/_static/wand.png - :width: 120 - :height: 120 - -Wand_ -===== - -Wand is a ``ctypes``-based simple ImageMagick_ binding for Python, -supporting 2.7, 3.3+, and PyPy. All functionalities of MagickWand API are -implemented in Wand. - -You can install the package from PyPI_ by using ``pip``: - -.. code-block:: console - - $ pip install Wand - -Or would you like to enjoy the bleeding edge? Check out the head -revision of the source code from the `GitHub repository`__: - -.. code-block:: console - - $ git clone git://github.com/emcconville/wand.git - $ cd wand/ - $ python setup.py install - -.. _Wand: http://wand-py.org/ -.. _ImageMagick: https://www.imagemagick.org/ -.. _PyPI: https://pypi.python.org/pypi/Wand -__ https://github.com/emcconville/wand - - -Docs ----- - -Recent version - https://docs.wand-py.org/ - -Development version - https://docs.wand-py.org/en/latest/ - - .. image:: https://readthedocs.org/projects/wand/badge/ - :alt: Documentation Status - :target: https://docs.wand-py.org/en/latest/ - - -Community ---------- - -Website - http://wand-py.org/ - -GitHub - https://github.com/emcconville/wand - -Package Index (Cheeseshop) - https://pypi.python.org/pypi/Wand - - .. image:: https://badge.fury.io/py/Wand.svg? - :alt: Latest PyPI version - :target: https://pypi.python.org/pypi/Wand - -Discord - https://discord.gg/wtDWDE9fXK - -Stack Overflow tag (Q&A) - http://stackoverflow.com/questions/tagged/wand - -Continuous Integration (Travis CI) - https://app.travis-ci.com/emcconville/wand - - .. image:: https://app.travis-ci.com/emcconville/wand.svg?branch=master - :alt: Build Status - :target: https://app.travis-ci.com/emcconville/wand - -Continuous Integration (GitHub Actions) - https://github.com/emcconville/wand/actions - - .. image:: https://github.com/emcconville/wand/workflows/Wand%20CI/badge.svg - :alt: Build Status - :target: https://github.com/emcconville/wand/actions?query=workflow%3A%22Wand+CI%22 - -Code Coverage - https://coveralls.io/r/emcconville/wand - - .. image:: https://coveralls.io/repos/github/emcconville/wand/badge.svg?branch=master - :target: https://coveralls.io/github/emcconville/wand?branch=master diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/RECORD b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/RECORD deleted file mode 100644 index 4bd2216..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/RECORD +++ /dev/null @@ -1,53 +0,0 @@ -Wand-0.6.11.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -Wand-0.6.11.dist-info/LICENSE,sha256=LApHI5GF4xKeFcpRi4lLV5DPNhJG7jO9M0B0PLsdr2c,1183 -Wand-0.6.11.dist-info/METADATA,sha256=4OhGj2YqdeL4-EuDmjzOIi8LUWzws4_ltcYLBtTl55k,3963 -Wand-0.6.11.dist-info/RECORD,, -Wand-0.6.11.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -Wand-0.6.11.dist-info/WHEEL,sha256=bb2Ot9scclHKMOLDEHY6B2sicWOgugjFKaJsT7vwMQo,110 -Wand-0.6.11.dist-info/top_level.txt,sha256=uFTymN2uxamdZLu2fxZzaBcGwv7WW9v60YcsATzndig,5 -wand/__init__.py,sha256=bEmSKTbdilJXM5PTgsuaqvpliBlmy2of5f77SJMKRh8,202 -wand/__pycache__/__init__.cpython-311.pyc,, -wand/__pycache__/api.cpython-311.pyc,, -wand/__pycache__/assertions.cpython-311.pyc,, -wand/__pycache__/color.cpython-311.pyc,, -wand/__pycache__/compat.cpython-311.pyc,, -wand/__pycache__/display.cpython-311.pyc,, -wand/__pycache__/drawing.cpython-311.pyc,, -wand/__pycache__/exceptions.cpython-311.pyc,, -wand/__pycache__/font.cpython-311.pyc,, -wand/__pycache__/image.cpython-311.pyc,, -wand/__pycache__/resource.cpython-311.pyc,, -wand/__pycache__/sequence.cpython-311.pyc,, -wand/__pycache__/version.cpython-311.pyc,, -wand/api.py,sha256=C25oD0Iw9ZxvH_qpul8OlhZBFc3wijAB_DGPhqFpOow,10059 -wand/assertions.py,sha256=PGaK60baSc6tX1y0gHw-0zzwDrR6hq-zPCB_fzpSZcs,4721 -wand/cdefs/__init__.py,sha256=YaCYVyNhimXKrD5xWLrAmaMsBAr0QrnVshC7b_vfIPE,126 -wand/cdefs/__pycache__/__init__.cpython-311.pyc,, -wand/cdefs/__pycache__/core.cpython-311.pyc,, -wand/cdefs/__pycache__/drawing_wand.cpython-311.pyc,, -wand/cdefs/__pycache__/magick_image.cpython-311.pyc,, -wand/cdefs/__pycache__/magick_property.cpython-311.pyc,, -wand/cdefs/__pycache__/magick_wand.cpython-311.pyc,, -wand/cdefs/__pycache__/pixel_iterator.cpython-311.pyc,, -wand/cdefs/__pycache__/pixel_wand.cpython-311.pyc,, -wand/cdefs/__pycache__/structures.cpython-311.pyc,, -wand/cdefs/__pycache__/wandtypes.cpython-311.pyc,, -wand/cdefs/core.py,sha256=eUl6bMbClcnJ9ioUguk1fGSjfifH-jBtc2p_9Jng58c,5782 -wand/cdefs/drawing_wand.py,sha256=ACmcsSE2qdntzbdwZpWoWMGkaqQsLfqAU1wpWx0uH8c,12342 -wand/cdefs/magick_image.py,sha256=S_IwxTkQqWv-Y6gLym9lLrnVPvcRFc3Fd81dxAx-624,53526 -wand/cdefs/magick_property.py,sha256=hwmInYEzoAC4cteqe1Z21WkD38XPWRZhAn8zQkjdWHc,8658 -wand/cdefs/magick_wand.py,sha256=wAMRQk6zwUnVnj1eLebquk9u3w2ZFhDgtmLGUWy4oP0,2476 -wand/cdefs/pixel_iterator.py,sha256=cnFCLD9Aj5BYtU4iIFJvgwb969WLimUG9DUMYYVpSU4,1796 -wand/cdefs/pixel_wand.py,sha256=SdS5ybfyHhSO3BIH5g3ff6v3bHNEzwDPGjLJab5_QdY,7553 -wand/cdefs/structures.py,sha256=yWbmqUUmZqLcKxK3iUMg5kbH36gsjhqPHtPdZpH1mlU,6786 -wand/cdefs/wandtypes.py,sha256=0_VgrY2IurGmaRcsPYFKVDPpqekRn4upQFZEXoQqoPw,1400 -wand/color.py,sha256=yZ0of-ltHGjdckMQ6FMSNXasq_VUQhpE-skvZtekGRM,24508 -wand/compat.py,sha256=wUj7zLA27zTtBdNYw1_RQ0NcmZi0VTvJG5kmfOjRx88,4570 -wand/display.py,sha256=nM8e52KEHRVJIhbWmCqlCSYtDa0JXwswedfP06_ToVI,2449 -wand/drawing.py,sha256=GkeDDXL1-IWwHMuZa3roBqlNz583yRJ5rvkoqrWBwUg,80106 -wand/exceptions.py,sha256=ZtD_15ij58SYXp7QXMxbXp8291vYH0k5MFQJPflICdU,11165 -wand/font.py,sha256=8auFsXmnLppE6TDvopXHCg430ZK6NkqEGqEkVkaPgsk,4021 -wand/image.py,sha256=qtpRKKghTUdYqQwMf5F6g1vzAgfL1OiRSm98sPpPptk,430445 -wand/resource.py,sha256=NrlAzL4QnyxeQp-uZS1WT4HA1kqf1y9QJ6tluZowHmg,11805 -wand/sequence.py,sha256=ewZnCuR7rOeLQTp5Ix34dxu5huiCEx5GUVjEdgDzDKU,13183 -wand/version.py,sha256=OhreUt7YGZKCj-HC6jfFjlqg8alSJKDnLoS0aaePu18,10693 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/REQUESTED b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/WHEEL b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/WHEEL deleted file mode 100644 index 9d8f872..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/WHEEL +++ /dev/null @@ -1,6 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.38.4) -Root-Is-Purelib: true -Tag: py2-none-any -Tag: py3-none-any - diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/top_level.txt b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/top_level.txt deleted file mode 100644 index 375e1d6..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/Wand-0.6.11.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -wand diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/INSTALLER b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/LICENSE b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/LICENSE deleted file mode 100644 index 4362b49..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/LICENSE +++ /dev/null @@ -1,502 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/METADATA b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/METADATA deleted file mode 100644 index 3ec4d79..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/METADATA +++ /dev/null @@ -1,97 +0,0 @@ -Metadata-Version: 2.1 -Name: chardet -Version: 5.2.0 -Summary: Universal encoding detector for Python 3 -Home-page: https://github.com/chardet/chardet -Author: Mark Pilgrim -Author-email: mark@diveintomark.org -Maintainer: Daniel Blanchard -Maintainer-email: dan.blanchard@gmail.com -License: LGPL -Project-URL: Documentation, https://chardet.readthedocs.io/ -Project-URL: GitHub Project, https://github.com/chardet/chardet -Project-URL: Issue Tracker, https://github.com/chardet/chardet/issues -Keywords: encoding,i18n,xml -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+) -Classifier: Operating System :: OS Independent -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: 3.11 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Classifier: Topic :: Software Development :: Libraries :: Python Modules -Classifier: Topic :: Text Processing :: Linguistic -Requires-Python: >=3.7 -License-File: LICENSE - -Chardet: The Universal Character Encoding Detector --------------------------------------------------- - -.. image:: https://img.shields.io/travis/chardet/chardet/stable.svg - :alt: Build status - :target: https://travis-ci.org/chardet/chardet - -.. image:: https://img.shields.io/coveralls/chardet/chardet/stable.svg - :target: https://coveralls.io/r/chardet/chardet - -.. image:: https://img.shields.io/pypi/v/chardet.svg - :target: https://warehouse.python.org/project/chardet/ - :alt: Latest version on PyPI - -.. image:: https://img.shields.io/pypi/l/chardet.svg - :alt: License - - -Detects - - ASCII, UTF-8, UTF-16 (2 variants), UTF-32 (4 variants) - - Big5, GB2312, EUC-TW, HZ-GB-2312, ISO-2022-CN (Traditional and Simplified Chinese) - - EUC-JP, SHIFT_JIS, CP932, ISO-2022-JP (Japanese) - - EUC-KR, ISO-2022-KR, Johab (Korean) - - KOI8-R, MacCyrillic, IBM855, IBM866, ISO-8859-5, windows-1251 (Cyrillic) - - ISO-8859-5, windows-1251 (Bulgarian) - - ISO-8859-1, windows-1252, MacRoman (Western European languages) - - ISO-8859-7, windows-1253 (Greek) - - ISO-8859-8, windows-1255 (Visual and Logical Hebrew) - - TIS-620 (Thai) - -.. note:: - Our ISO-8859-2 and windows-1250 (Hungarian) probers have been temporarily - disabled until we can retrain the models. - -Requires Python 3.7+. - -Installation ------------- - -Install from `PyPI `_:: - - pip install chardet - -Documentation -------------- - -For users, docs are now available at https://chardet.readthedocs.io/. - -Command-line Tool ------------------ - -chardet comes with a command-line script which reports on the encodings of one -or more files:: - - % chardetect somefile someotherfile - somefile: windows-1252 with confidence 0.5 - someotherfile: ascii with confidence 1.0 - -About ------ - -This is a continuation of Mark Pilgrim's excellent original chardet port from C, and `Ian Cordasco `_'s -`charade `_ Python 3-compatible fork. - -:maintainer: Dan Blanchard diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/RECORD b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/RECORD deleted file mode 100644 index 5edb3df..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/RECORD +++ /dev/null @@ -1,108 +0,0 @@ -../../Scripts/chardetect.exe,sha256=TTcCkRo4OveAQnKdC60CKfLw9ZexsgFMv26uhCwJR2E,108468 -chardet-5.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -chardet-5.2.0.dist-info/LICENSE,sha256=3GJlINzVOiL3J68-5Cx3DlbJemT-OtsGN5nYqwMv5VE,26530 -chardet-5.2.0.dist-info/METADATA,sha256=PAr2NQ6hQWpjyFnwlI7MoxHt2S_6oRiUsucOKMNhzGw,3418 -chardet-5.2.0.dist-info/RECORD,, -chardet-5.2.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -chardet-5.2.0.dist-info/WHEEL,sha256=AtBG6SXL3KF_v0NxLf0ehyVOh0cold-JbJYXNGorC6Q,92 -chardet-5.2.0.dist-info/entry_points.txt,sha256=_cdvYc4jyY68GYfsQAAthNMxO-yodcGkvNC1xOEsLmI,59 -chardet-5.2.0.dist-info/top_level.txt,sha256=AowzBbZy4x8EirABDdJSLJZMkJ_53iIag8xfKR6D7kI,8 -chardet/__init__.py,sha256=57R-HSxj0PWmILMN0GFmUNqEMfrEVSamXyjD-W6_fbs,4797 -chardet/__main__.py,sha256=puNj2o_QfBRKElEkiVp1zEIL1gGYD2o-JuXLFlqHDC4,123 -chardet/__pycache__/__init__.cpython-311.pyc,, -chardet/__pycache__/__main__.cpython-311.pyc,, -chardet/__pycache__/big5freq.cpython-311.pyc,, -chardet/__pycache__/big5prober.cpython-311.pyc,, -chardet/__pycache__/chardistribution.cpython-311.pyc,, -chardet/__pycache__/charsetgroupprober.cpython-311.pyc,, -chardet/__pycache__/charsetprober.cpython-311.pyc,, -chardet/__pycache__/codingstatemachine.cpython-311.pyc,, -chardet/__pycache__/codingstatemachinedict.cpython-311.pyc,, -chardet/__pycache__/cp949prober.cpython-311.pyc,, -chardet/__pycache__/enums.cpython-311.pyc,, -chardet/__pycache__/escprober.cpython-311.pyc,, -chardet/__pycache__/escsm.cpython-311.pyc,, -chardet/__pycache__/eucjpprober.cpython-311.pyc,, -chardet/__pycache__/euckrfreq.cpython-311.pyc,, -chardet/__pycache__/euckrprober.cpython-311.pyc,, -chardet/__pycache__/euctwfreq.cpython-311.pyc,, -chardet/__pycache__/euctwprober.cpython-311.pyc,, -chardet/__pycache__/gb2312freq.cpython-311.pyc,, -chardet/__pycache__/gb2312prober.cpython-311.pyc,, -chardet/__pycache__/hebrewprober.cpython-311.pyc,, -chardet/__pycache__/jisfreq.cpython-311.pyc,, -chardet/__pycache__/johabfreq.cpython-311.pyc,, -chardet/__pycache__/johabprober.cpython-311.pyc,, -chardet/__pycache__/jpcntx.cpython-311.pyc,, -chardet/__pycache__/langbulgarianmodel.cpython-311.pyc,, -chardet/__pycache__/langgreekmodel.cpython-311.pyc,, -chardet/__pycache__/langhebrewmodel.cpython-311.pyc,, -chardet/__pycache__/langhungarianmodel.cpython-311.pyc,, -chardet/__pycache__/langrussianmodel.cpython-311.pyc,, -chardet/__pycache__/langthaimodel.cpython-311.pyc,, -chardet/__pycache__/langturkishmodel.cpython-311.pyc,, -chardet/__pycache__/latin1prober.cpython-311.pyc,, -chardet/__pycache__/macromanprober.cpython-311.pyc,, -chardet/__pycache__/mbcharsetprober.cpython-311.pyc,, -chardet/__pycache__/mbcsgroupprober.cpython-311.pyc,, -chardet/__pycache__/mbcssm.cpython-311.pyc,, -chardet/__pycache__/resultdict.cpython-311.pyc,, -chardet/__pycache__/sbcharsetprober.cpython-311.pyc,, -chardet/__pycache__/sbcsgroupprober.cpython-311.pyc,, -chardet/__pycache__/sjisprober.cpython-311.pyc,, -chardet/__pycache__/universaldetector.cpython-311.pyc,, -chardet/__pycache__/utf1632prober.cpython-311.pyc,, -chardet/__pycache__/utf8prober.cpython-311.pyc,, -chardet/__pycache__/version.cpython-311.pyc,, -chardet/big5freq.py,sha256=ltcfP-3PjlNHCoo5e4a7C4z-2DhBTXRfY6jbMbB7P30,31274 -chardet/big5prober.py,sha256=lPMfwCX6v2AaPgvFh_cSWZcgLDbWiFCHLZ_p9RQ9uxE,1763 -chardet/chardistribution.py,sha256=13B8XUG4oXDuLdXvfbIWwLFeR-ZU21AqTS1zcdON8bU,10032 -chardet/charsetgroupprober.py,sha256=UKK3SaIZB2PCdKSIS0gnvMtLR9JJX62M-fZJu3OlWyg,3915 -chardet/charsetprober.py,sha256=L3t8_wIOov8em-vZWOcbkdsrwe43N6_gqNh5pH7WPd4,5420 -chardet/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -chardet/cli/__pycache__/__init__.cpython-311.pyc,, -chardet/cli/__pycache__/chardetect.cpython-311.pyc,, -chardet/cli/chardetect.py,sha256=zibMVg5RpKb-ME9_7EYG4ZM2Sf07NHcQzZ12U-rYJho,3242 -chardet/codingstatemachine.py,sha256=K7k69sw3jY5DmTXoSJQVsUtFIQKYPQVOSJJhBuGv_yE,3732 -chardet/codingstatemachinedict.py,sha256=0GY3Hi2qIZvDrOOJ3AtqppM1RsYxr_66ER4EHjuMiMc,542 -chardet/cp949prober.py,sha256=0jKRV7fECuWI16rNnks0ZECKA1iZYCIEaP8A1ZvjUSI,1860 -chardet/enums.py,sha256=TzECiZoCKNMqgwU76cPCeKWFBqaWvAdLMev5_bCkhY8,1683 -chardet/escprober.py,sha256=Kho48X65xE0scFylIdeJjM2bcbvRvv0h0WUbMWrJD3A,4006 -chardet/escsm.py,sha256=AqyXpA2FQFD7k-buBty_7itGEYkhmVa8X09NLRul3QM,12176 -chardet/eucjpprober.py,sha256=5KYaM9fsxkRYzw1b5k0fL-j_-ezIw-ij9r97a9MHxLY,3934 -chardet/euckrfreq.py,sha256=3mHuRvXfsq_QcQysDQFb8qSudvTiol71C6Ic2w57tKM,13566 -chardet/euckrprober.py,sha256=hiFT6wM174GIwRvqDsIcuOc-dDsq2uPKMKbyV8-1Xnc,1753 -chardet/euctwfreq.py,sha256=2alILE1Lh5eqiFJZjzRkMQXolNJRHY5oBQd-vmZYFFM,36913 -chardet/euctwprober.py,sha256=NxbpNdBtU0VFI0bKfGfDkpP7S2_8_6FlO87dVH0ogws,1753 -chardet/gb2312freq.py,sha256=49OrdXzD-HXqwavkqjo8Z7gvs58hONNzDhAyMENNkvY,20735 -chardet/gb2312prober.py,sha256=KPEBueaSLSvBpFeINMu0D6TgHcR90e5PaQawifzF4o0,1759 -chardet/hebrewprober.py,sha256=96T_Lj_OmW-fK7JrSHojYjyG3fsGgbzkoTNleZ3kfYE,14537 -chardet/jisfreq.py,sha256=mm8tfrwqhpOd3wzZKS4NJqkYBQVcDfTM2JiQ5aW932E,25796 -chardet/johabfreq.py,sha256=dBpOYG34GRX6SL8k_LbS9rxZPMjLjoMlgZ03Pz5Hmqc,42498 -chardet/johabprober.py,sha256=O1Qw9nVzRnun7vZp4UZM7wvJSv9W941mEU9uDMnY3DU,1752 -chardet/jpcntx.py,sha256=uhHrYWkLxE_rF5OkHKInm0HUsrjgKHHVQvtt3UcvotA,27055 -chardet/langbulgarianmodel.py,sha256=bGoRpxBYtrbSHa6mX6PkEA26v30pWmhDjemhdxmkew8,104550 -chardet/langgreekmodel.py,sha256=3wMlEzQ8oU2MbrL2xN8lkuOB0dCMLBhW6heekxusoc0,98472 -chardet/langhebrewmodel.py,sha256=ZUTqusxMvR_earWPs5w-rH10xoe5sPjd9FLMu1DUIvE,98184 -chardet/langhungarianmodel.py,sha256=N-YtC2EiswyS7XsUicCPRycrIzRNj47Y048odp9qOoo,101351 -chardet/langrussianmodel.py,sha256=6v7RcZKGj0VH0864BHzizKNceAYbHvGts2p00ifC7w4,128023 -chardet/langthaimodel.py,sha256=Mr673U9U8rkQFfUDtLP01pp-0TOsl2o6sb75YEjvpcs,102762 -chardet/langturkishmodel.py,sha256=LkXCjWhGUEzqKXvfasHN0SFBigwKJ3xeWNVZ0EyI0kA,95360 -chardet/latin1prober.py,sha256=p15EEmFbmQUwbKLC7lOJVGHEZwcG45ubEZYTGu01J5g,5380 -chardet/macromanprober.py,sha256=9anfzmY6TBfUPDyBDOdY07kqmTHpZ1tK0jL-p1JWcOY,6077 -chardet/mbcharsetprober.py,sha256=Wr04WNI4F3X_VxEverNG-H25g7u-MDDKlNt-JGj-_uU,3715 -chardet/mbcsgroupprober.py,sha256=iRpaNBjV0DNwYPu_z6TiHgRpwYahiM7ztI_4kZ4Uz9A,2131 -chardet/mbcssm.py,sha256=hUtPvDYgWDaA2dWdgLsshbwRfm3Q5YRlRogdmeRUNQw,30391 -chardet/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -chardet/metadata/__pycache__/__init__.cpython-311.pyc,, -chardet/metadata/__pycache__/languages.cpython-311.pyc,, -chardet/metadata/languages.py,sha256=FhvBIdZFxRQ-dTwkb_0madRKgVBCaUMQz9I5xqjE5iQ,13560 -chardet/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -chardet/resultdict.py,sha256=ez4FRvN5KaSosJeJ2WzUyKdDdg35HDy_SSLPXKCdt5M,402 -chardet/sbcharsetprober.py,sha256=-nd3F90i7GpXLjehLVHqVBE0KlWzGvQUPETLBNn4o6U,6400 -chardet/sbcsgroupprober.py,sha256=gcgI0fOfgw_3YTClpbra_MNxwyEyJ3eUXraoLHYb59E,4137 -chardet/sjisprober.py,sha256=aqQufMzRw46ZpFlzmYaYeT2-nzmKb-hmcrApppJ862k,4007 -chardet/universaldetector.py,sha256=xYBrg4x0dd9WnT8qclfADVD9ondrUNkqPmvte1pa520,14848 -chardet/utf1632prober.py,sha256=pw1epGdMj1hDGiCu1AHqqzOEfjX8MVdiW7O1BlT8-eQ,8505 -chardet/utf8prober.py,sha256=8m08Ub5490H4jQ6LYXvFysGtgKoKsHUd2zH_i8_TnVw,2812 -chardet/version.py,sha256=jp8ePp1zC63YxruGcHSuKxtf3-fF1LYAMUZD2eDWYok,244 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/REQUESTED b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/REQUESTED deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/WHEEL b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/WHEEL deleted file mode 100644 index d272f6e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/WHEEL +++ /dev/null @@ -1,5 +0,0 @@ -Wheel-Version: 1.0 -Generator: bdist_wheel (0.41.0) -Root-Is-Purelib: true -Tag: py3-none-any - diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/entry_points.txt b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/entry_points.txt deleted file mode 100644 index c36a5e3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/entry_points.txt +++ /dev/null @@ -1,2 +0,0 @@ -[console_scripts] -chardetect = chardet.cli.chardetect:main diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/top_level.txt b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/top_level.txt deleted file mode 100644 index 79236f2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet-5.2.0.dist-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -chardet diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/__init__.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/__init__.py deleted file mode 100644 index fe58162..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/__init__.py +++ /dev/null @@ -1,115 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import List, Union - -from .charsetgroupprober import CharSetGroupProber -from .charsetprober import CharSetProber -from .enums import InputState -from .resultdict import ResultDict -from .universaldetector import UniversalDetector -from .version import VERSION, __version__ - -__all__ = ["UniversalDetector", "detect", "detect_all", "__version__", "VERSION"] - - -def detect( - byte_str: Union[bytes, bytearray], should_rename_legacy: bool = False -) -> ResultDict: - """ - Detect the encoding of the given byte string. - - :param byte_str: The byte sequence to examine. - :type byte_str: ``bytes`` or ``bytearray`` - :param should_rename_legacy: Should we rename legacy encodings - to their more modern equivalents? - :type should_rename_legacy: ``bool`` - """ - if not isinstance(byte_str, bytearray): - if not isinstance(byte_str, bytes): - raise TypeError( - f"Expected object of type bytes or bytearray, got: {type(byte_str)}" - ) - byte_str = bytearray(byte_str) - detector = UniversalDetector(should_rename_legacy=should_rename_legacy) - detector.feed(byte_str) - return detector.close() - - -def detect_all( - byte_str: Union[bytes, bytearray], - ignore_threshold: bool = False, - should_rename_legacy: bool = False, -) -> List[ResultDict]: - """ - Detect all the possible encodings of the given byte string. - - :param byte_str: The byte sequence to examine. - :type byte_str: ``bytes`` or ``bytearray`` - :param ignore_threshold: Include encodings that are below - ``UniversalDetector.MINIMUM_THRESHOLD`` - in results. - :type ignore_threshold: ``bool`` - :param should_rename_legacy: Should we rename legacy encodings - to their more modern equivalents? - :type should_rename_legacy: ``bool`` - """ - if not isinstance(byte_str, bytearray): - if not isinstance(byte_str, bytes): - raise TypeError( - f"Expected object of type bytes or bytearray, got: {type(byte_str)}" - ) - byte_str = bytearray(byte_str) - - detector = UniversalDetector(should_rename_legacy=should_rename_legacy) - detector.feed(byte_str) - detector.close() - - if detector.input_state == InputState.HIGH_BYTE: - results: List[ResultDict] = [] - probers: List[CharSetProber] = [] - for prober in detector.charset_probers: - if isinstance(prober, CharSetGroupProber): - probers.extend(p for p in prober.probers) - else: - probers.append(prober) - for prober in probers: - if ignore_threshold or prober.get_confidence() > detector.MINIMUM_THRESHOLD: - charset_name = prober.charset_name or "" - lower_charset_name = charset_name.lower() - # Use Windows encoding name instead of ISO-8859 if we saw any - # extra Windows-specific bytes - if lower_charset_name.startswith("iso-8859") and detector.has_win_bytes: - charset_name = detector.ISO_WIN_MAP.get( - lower_charset_name, charset_name - ) - # Rename legacy encodings with superset encodings if asked - if should_rename_legacy: - charset_name = detector.LEGACY_MAP.get( - charset_name.lower(), charset_name - ) - results.append( - { - "encoding": charset_name, - "confidence": prober.get_confidence(), - "language": prober.language, - } - ) - if len(results) > 0: - return sorted(results, key=lambda result: -result["confidence"]) - - return [detector.result] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/__main__.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/__main__.py deleted file mode 100644 index c19b0d2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/__main__.py +++ /dev/null @@ -1,6 +0,0 @@ -"""Wrapper so people can run python -m chardet""" - -from .cli.chardetect import main - -if __name__ == "__main__": - main() diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/big5freq.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/big5freq.py deleted file mode 100644 index 87d9f97..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/big5freq.py +++ /dev/null @@ -1,386 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# Big5 frequency table -# by Taiwan's Mandarin Promotion Council -# -# -# 128 --> 0.42261 -# 256 --> 0.57851 -# 512 --> 0.74851 -# 1024 --> 0.89384 -# 2048 --> 0.97583 -# -# Ideal Distribution Ratio = 0.74851/(1-0.74851) =2.98 -# Random Distribution Ration = 512/(5401-512)=0.105 -# -# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR - -BIG5_TYPICAL_DISTRIBUTION_RATIO = 0.75 - -# Char to FreqOrder table -BIG5_TABLE_SIZE = 5376 -# fmt: off -BIG5_CHAR_TO_FREQ_ORDER = ( - 1,1801,1506, 255,1431, 198, 9, 82, 6,5008, 177, 202,3681,1256,2821, 110, # 16 -3814, 33,3274, 261, 76, 44,2114, 16,2946,2187,1176, 659,3971, 26,3451,2653, # 32 -1198,3972,3350,4202, 410,2215, 302, 590, 361,1964, 8, 204, 58,4510,5009,1932, # 48 - 63,5010,5011, 317,1614, 75, 222, 159,4203,2417,1480,5012,3555,3091, 224,2822, # 64 -3682, 3, 10,3973,1471, 29,2787,1135,2866,1940, 873, 130,3275,1123, 312,5013, # 80 -4511,2052, 507, 252, 682,5014, 142,1915, 124, 206,2947, 34,3556,3204, 64, 604, # 96 -5015,2501,1977,1978, 155,1991, 645, 641,1606,5016,3452, 337, 72, 406,5017, 80, # 112 - 630, 238,3205,1509, 263, 939,1092,2654, 756,1440,1094,3453, 449, 69,2987, 591, # 128 - 179,2096, 471, 115,2035,1844, 60, 50,2988, 134, 806,1869, 734,2036,3454, 180, # 144 - 995,1607, 156, 537,2907, 688,5018, 319,1305, 779,2145, 514,2379, 298,4512, 359, # 160 -2502, 90,2716,1338, 663, 11, 906,1099,2553, 20,2441, 182, 532,1716,5019, 732, # 176 -1376,4204,1311,1420,3206, 25,2317,1056, 113, 399, 382,1950, 242,3455,2474, 529, # 192 -3276, 475,1447,3683,5020, 117, 21, 656, 810,1297,2300,2334,3557,5021, 126,4205, # 208 - 706, 456, 150, 613,4513, 71,1118,2037,4206, 145,3092, 85, 835, 486,2115,1246, # 224 -1426, 428, 727,1285,1015, 800, 106, 623, 303,1281,5022,2128,2359, 347,3815, 221, # 240 -3558,3135,5023,1956,1153,4207, 83, 296,1199,3093, 192, 624, 93,5024, 822,1898, # 256 -2823,3136, 795,2065, 991,1554,1542,1592, 27, 43,2867, 859, 139,1456, 860,4514, # 272 - 437, 712,3974, 164,2397,3137, 695, 211,3037,2097, 195,3975,1608,3559,3560,3684, # 288 -3976, 234, 811,2989,2098,3977,2233,1441,3561,1615,2380, 668,2077,1638, 305, 228, # 304 -1664,4515, 467, 415,5025, 262,2099,1593, 239, 108, 300, 200,1033, 512,1247,2078, # 320 -5026,5027,2176,3207,3685,2682, 593, 845,1062,3277, 88,1723,2038,3978,1951, 212, # 336 - 266, 152, 149, 468,1899,4208,4516, 77, 187,5028,3038, 37, 5,2990,5029,3979, # 352 -5030,5031, 39,2524,4517,2908,3208,2079, 55, 148, 74,4518, 545, 483,1474,1029, # 368 -1665, 217,1870,1531,3138,1104,2655,4209, 24, 172,3562, 900,3980,3563,3564,4519, # 384 - 32,1408,2824,1312, 329, 487,2360,2251,2717, 784,2683, 4,3039,3351,1427,1789, # 400 - 188, 109, 499,5032,3686,1717,1790, 888,1217,3040,4520,5033,3565,5034,3352,1520, # 416 -3687,3981, 196,1034, 775,5035,5036, 929,1816, 249, 439, 38,5037,1063,5038, 794, # 432 -3982,1435,2301, 46, 178,3278,2066,5039,2381,5040, 214,1709,4521, 804, 35, 707, # 448 - 324,3688,1601,2554, 140, 459,4210,5041,5042,1365, 839, 272, 978,2262,2580,3456, # 464 -2129,1363,3689,1423, 697, 100,3094, 48, 70,1231, 495,3139,2196,5043,1294,5044, # 480 -2080, 462, 586,1042,3279, 853, 256, 988, 185,2382,3457,1698, 434,1084,5045,3458, # 496 - 314,2625,2788,4522,2335,2336, 569,2285, 637,1817,2525, 757,1162,1879,1616,3459, # 512 - 287,1577,2116, 768,4523,1671,2868,3566,2526,1321,3816, 909,2418,5046,4211, 933, # 528 -3817,4212,2053,2361,1222,4524, 765,2419,1322, 786,4525,5047,1920,1462,1677,2909, # 544 -1699,5048,4526,1424,2442,3140,3690,2600,3353,1775,1941,3460,3983,4213, 309,1369, # 560 -1130,2825, 364,2234,1653,1299,3984,3567,3985,3986,2656, 525,1085,3041, 902,2001, # 576 -1475, 964,4527, 421,1845,1415,1057,2286, 940,1364,3141, 376,4528,4529,1381, 7, # 592 -2527, 983,2383, 336,1710,2684,1846, 321,3461, 559,1131,3042,2752,1809,1132,1313, # 608 - 265,1481,1858,5049, 352,1203,2826,3280, 167,1089, 420,2827, 776, 792,1724,3568, # 624 -4214,2443,3281,5050,4215,5051, 446, 229, 333,2753, 901,3818,1200,1557,4530,2657, # 640 -1921, 395,2754,2685,3819,4216,1836, 125, 916,3209,2626,4531,5052,5053,3820,5054, # 656 -5055,5056,4532,3142,3691,1133,2555,1757,3462,1510,2318,1409,3569,5057,2146, 438, # 672 -2601,2910,2384,3354,1068, 958,3043, 461, 311,2869,2686,4217,1916,3210,4218,1979, # 688 - 383, 750,2755,2627,4219, 274, 539, 385,1278,1442,5058,1154,1965, 384, 561, 210, # 704 - 98,1295,2556,3570,5059,1711,2420,1482,3463,3987,2911,1257, 129,5060,3821, 642, # 720 - 523,2789,2790,2658,5061, 141,2235,1333, 68, 176, 441, 876, 907,4220, 603,2602, # 736 - 710, 171,3464, 404, 549, 18,3143,2398,1410,3692,1666,5062,3571,4533,2912,4534, # 752 -5063,2991, 368,5064, 146, 366, 99, 871,3693,1543, 748, 807,1586,1185, 22,2263, # 768 - 379,3822,3211,5065,3212, 505,1942,2628,1992,1382,2319,5066, 380,2362, 218, 702, # 784 -1818,1248,3465,3044,3572,3355,3282,5067,2992,3694, 930,3283,3823,5068, 59,5069, # 800 - 585, 601,4221, 497,3466,1112,1314,4535,1802,5070,1223,1472,2177,5071, 749,1837, # 816 - 690,1900,3824,1773,3988,1476, 429,1043,1791,2236,2117, 917,4222, 447,1086,1629, # 832 -5072, 556,5073,5074,2021,1654, 844,1090, 105, 550, 966,1758,2828,1008,1783, 686, # 848 -1095,5075,2287, 793,1602,5076,3573,2603,4536,4223,2948,2302,4537,3825, 980,2503, # 864 - 544, 353, 527,4538, 908,2687,2913,5077, 381,2629,1943,1348,5078,1341,1252, 560, # 880 -3095,5079,3467,2870,5080,2054, 973, 886,2081, 143,4539,5081,5082, 157,3989, 496, # 896 -4224, 57, 840, 540,2039,4540,4541,3468,2118,1445, 970,2264,1748,1966,2082,4225, # 912 -3144,1234,1776,3284,2829,3695, 773,1206,2130,1066,2040,1326,3990,1738,1725,4226, # 928 - 279,3145, 51,1544,2604, 423,1578,2131,2067, 173,4542,1880,5083,5084,1583, 264, # 944 - 610,3696,4543,2444, 280, 154,5085,5086,5087,1739, 338,1282,3096, 693,2871,1411, # 960 -1074,3826,2445,5088,4544,5089,5090,1240, 952,2399,5091,2914,1538,2688, 685,1483, # 976 -4227,2475,1436, 953,4228,2055,4545, 671,2400, 79,4229,2446,3285, 608, 567,2689, # 992 -3469,4230,4231,1691, 393,1261,1792,2401,5092,4546,5093,5094,5095,5096,1383,1672, # 1008 -3827,3213,1464, 522,1119, 661,1150, 216, 675,4547,3991,1432,3574, 609,4548,2690, # 1024 -2402,5097,5098,5099,4232,3045, 0,5100,2476, 315, 231,2447, 301,3356,4549,2385, # 1040 -5101, 233,4233,3697,1819,4550,4551,5102, 96,1777,1315,2083,5103, 257,5104,1810, # 1056 -3698,2718,1139,1820,4234,2022,1124,2164,2791,1778,2659,5105,3097, 363,1655,3214, # 1072 -5106,2993,5107,5108,5109,3992,1567,3993, 718, 103,3215, 849,1443, 341,3357,2949, # 1088 -1484,5110,1712, 127, 67, 339,4235,2403, 679,1412, 821,5111,5112, 834, 738, 351, # 1104 -2994,2147, 846, 235,1497,1881, 418,1993,3828,2719, 186,1100,2148,2756,3575,1545, # 1120 -1355,2950,2872,1377, 583,3994,4236,2581,2995,5113,1298,3699,1078,2557,3700,2363, # 1136 - 78,3829,3830, 267,1289,2100,2002,1594,4237, 348, 369,1274,2197,2178,1838,4552, # 1152 -1821,2830,3701,2757,2288,2003,4553,2951,2758, 144,3358, 882,4554,3995,2759,3470, # 1168 -4555,2915,5114,4238,1726, 320,5115,3996,3046, 788,2996,5116,2831,1774,1327,2873, # 1184 -3997,2832,5117,1306,4556,2004,1700,3831,3576,2364,2660, 787,2023, 506, 824,3702, # 1200 - 534, 323,4557,1044,3359,2024,1901, 946,3471,5118,1779,1500,1678,5119,1882,4558, # 1216 - 165, 243,4559,3703,2528, 123, 683,4239, 764,4560, 36,3998,1793, 589,2916, 816, # 1232 - 626,1667,3047,2237,1639,1555,1622,3832,3999,5120,4000,2874,1370,1228,1933, 891, # 1248 -2084,2917, 304,4240,5121, 292,2997,2720,3577, 691,2101,4241,1115,4561, 118, 662, # 1264 -5122, 611,1156, 854,2386,1316,2875, 2, 386, 515,2918,5123,5124,3286, 868,2238, # 1280 -1486, 855,2661, 785,2216,3048,5125,1040,3216,3578,5126,3146, 448,5127,1525,5128, # 1296 -2165,4562,5129,3833,5130,4242,2833,3579,3147, 503, 818,4001,3148,1568, 814, 676, # 1312 -1444, 306,1749,5131,3834,1416,1030, 197,1428, 805,2834,1501,4563,5132,5133,5134, # 1328 -1994,5135,4564,5136,5137,2198, 13,2792,3704,2998,3149,1229,1917,5138,3835,2132, # 1344 -5139,4243,4565,2404,3580,5140,2217,1511,1727,1120,5141,5142, 646,3836,2448, 307, # 1360 -5143,5144,1595,3217,5145,5146,5147,3705,1113,1356,4002,1465,2529,2530,5148, 519, # 1376 -5149, 128,2133, 92,2289,1980,5150,4003,1512, 342,3150,2199,5151,2793,2218,1981, # 1392 -3360,4244, 290,1656,1317, 789, 827,2365,5152,3837,4566, 562, 581,4004,5153, 401, # 1408 -4567,2252, 94,4568,5154,1399,2794,5155,1463,2025,4569,3218,1944,5156, 828,1105, # 1424 -4245,1262,1394,5157,4246, 605,4570,5158,1784,2876,5159,2835, 819,2102, 578,2200, # 1440 -2952,5160,1502, 436,3287,4247,3288,2836,4005,2919,3472,3473,5161,2721,2320,5162, # 1456 -5163,2337,2068, 23,4571, 193, 826,3838,2103, 699,1630,4248,3098, 390,1794,1064, # 1472 -3581,5164,1579,3099,3100,1400,5165,4249,1839,1640,2877,5166,4572,4573, 137,4250, # 1488 - 598,3101,1967, 780, 104, 974,2953,5167, 278, 899, 253, 402, 572, 504, 493,1339, # 1504 -5168,4006,1275,4574,2582,2558,5169,3706,3049,3102,2253, 565,1334,2722, 863, 41, # 1520 -5170,5171,4575,5172,1657,2338, 19, 463,2760,4251, 606,5173,2999,3289,1087,2085, # 1536 -1323,2662,3000,5174,1631,1623,1750,4252,2691,5175,2878, 791,2723,2663,2339, 232, # 1552 -2421,5176,3001,1498,5177,2664,2630, 755,1366,3707,3290,3151,2026,1609, 119,1918, # 1568 -3474, 862,1026,4253,5178,4007,3839,4576,4008,4577,2265,1952,2477,5179,1125, 817, # 1584 -4254,4255,4009,1513,1766,2041,1487,4256,3050,3291,2837,3840,3152,5180,5181,1507, # 1600 -5182,2692, 733, 40,1632,1106,2879, 345,4257, 841,2531, 230,4578,3002,1847,3292, # 1616 -3475,5183,1263, 986,3476,5184, 735, 879, 254,1137, 857, 622,1300,1180,1388,1562, # 1632 -4010,4011,2954, 967,2761,2665,1349, 592,2134,1692,3361,3003,1995,4258,1679,4012, # 1648 -1902,2188,5185, 739,3708,2724,1296,1290,5186,4259,2201,2202,1922,1563,2605,2559, # 1664 -1871,2762,3004,5187, 435,5188, 343,1108, 596, 17,1751,4579,2239,3477,3709,5189, # 1680 -4580, 294,3582,2955,1693, 477, 979, 281,2042,3583, 643,2043,3710,2631,2795,2266, # 1696 -1031,2340,2135,2303,3584,4581, 367,1249,2560,5190,3585,5191,4582,1283,3362,2005, # 1712 - 240,1762,3363,4583,4584, 836,1069,3153, 474,5192,2149,2532, 268,3586,5193,3219, # 1728 -1521,1284,5194,1658,1546,4260,5195,3587,3588,5196,4261,3364,2693,1685,4262, 961, # 1744 -1673,2632, 190,2006,2203,3841,4585,4586,5197, 570,2504,3711,1490,5198,4587,2633, # 1760 -3293,1957,4588, 584,1514, 396,1045,1945,5199,4589,1968,2449,5200,5201,4590,4013, # 1776 - 619,5202,3154,3294, 215,2007,2796,2561,3220,4591,3221,4592, 763,4263,3842,4593, # 1792 -5203,5204,1958,1767,2956,3365,3712,1174, 452,1477,4594,3366,3155,5205,2838,1253, # 1808 -2387,2189,1091,2290,4264, 492,5206, 638,1169,1825,2136,1752,4014, 648, 926,1021, # 1824 -1324,4595, 520,4596, 997, 847,1007, 892,4597,3843,2267,1872,3713,2405,1785,4598, # 1840 -1953,2957,3103,3222,1728,4265,2044,3714,4599,2008,1701,3156,1551, 30,2268,4266, # 1856 -5207,2027,4600,3589,5208, 501,5209,4267, 594,3478,2166,1822,3590,3479,3591,3223, # 1872 - 829,2839,4268,5210,1680,3157,1225,4269,5211,3295,4601,4270,3158,2341,5212,4602, # 1888 -4271,5213,4015,4016,5214,1848,2388,2606,3367,5215,4603, 374,4017, 652,4272,4273, # 1904 - 375,1140, 798,5216,5217,5218,2366,4604,2269, 546,1659, 138,3051,2450,4605,5219, # 1920 -2254, 612,1849, 910, 796,3844,1740,1371, 825,3845,3846,5220,2920,2562,5221, 692, # 1936 - 444,3052,2634, 801,4606,4274,5222,1491, 244,1053,3053,4275,4276, 340,5223,4018, # 1952 -1041,3005, 293,1168, 87,1357,5224,1539, 959,5225,2240, 721, 694,4277,3847, 219, # 1968 -1478, 644,1417,3368,2666,1413,1401,1335,1389,4019,5226,5227,3006,2367,3159,1826, # 1984 - 730,1515, 184,2840, 66,4607,5228,1660,2958, 246,3369, 378,1457, 226,3480, 975, # 2000 -4020,2959,1264,3592, 674, 696,5229, 163,5230,1141,2422,2167, 713,3593,3370,4608, # 2016 -4021,5231,5232,1186, 15,5233,1079,1070,5234,1522,3224,3594, 276,1050,2725, 758, # 2032 -1126, 653,2960,3296,5235,2342, 889,3595,4022,3104,3007, 903,1250,4609,4023,3481, # 2048 -3596,1342,1681,1718, 766,3297, 286, 89,2961,3715,5236,1713,5237,2607,3371,3008, # 2064 -5238,2962,2219,3225,2880,5239,4610,2505,2533, 181, 387,1075,4024, 731,2190,3372, # 2080 -5240,3298, 310, 313,3482,2304, 770,4278, 54,3054, 189,4611,3105,3848,4025,5241, # 2096 -1230,1617,1850, 355,3597,4279,4612,3373, 111,4280,3716,1350,3160,3483,3055,4281, # 2112 -2150,3299,3598,5242,2797,4026,4027,3009, 722,2009,5243,1071, 247,1207,2343,2478, # 2128 -1378,4613,2010, 864,1437,1214,4614, 373,3849,1142,2220, 667,4615, 442,2763,2563, # 2144 -3850,4028,1969,4282,3300,1840, 837, 170,1107, 934,1336,1883,5244,5245,2119,4283, # 2160 -2841, 743,1569,5246,4616,4284, 582,2389,1418,3484,5247,1803,5248, 357,1395,1729, # 2176 -3717,3301,2423,1564,2241,5249,3106,3851,1633,4617,1114,2086,4285,1532,5250, 482, # 2192 -2451,4618,5251,5252,1492, 833,1466,5253,2726,3599,1641,2842,5254,1526,1272,3718, # 2208 -4286,1686,1795, 416,2564,1903,1954,1804,5255,3852,2798,3853,1159,2321,5256,2881, # 2224 -4619,1610,1584,3056,2424,2764, 443,3302,1163,3161,5257,5258,4029,5259,4287,2506, # 2240 -3057,4620,4030,3162,2104,1647,3600,2011,1873,4288,5260,4289, 431,3485,5261, 250, # 2256 - 97, 81,4290,5262,1648,1851,1558, 160, 848,5263, 866, 740,1694,5264,2204,2843, # 2272 -3226,4291,4621,3719,1687, 950,2479, 426, 469,3227,3720,3721,4031,5265,5266,1188, # 2288 - 424,1996, 861,3601,4292,3854,2205,2694, 168,1235,3602,4293,5267,2087,1674,4622, # 2304 -3374,3303, 220,2565,1009,5268,3855, 670,3010, 332,1208, 717,5269,5270,3603,2452, # 2320 -4032,3375,5271, 513,5272,1209,2882,3376,3163,4623,1080,5273,5274,5275,5276,2534, # 2336 -3722,3604, 815,1587,4033,4034,5277,3605,3486,3856,1254,4624,1328,3058,1390,4035, # 2352 -1741,4036,3857,4037,5278, 236,3858,2453,3304,5279,5280,3723,3859,1273,3860,4625, # 2368 -5281, 308,5282,4626, 245,4627,1852,2480,1307,2583, 430, 715,2137,2454,5283, 270, # 2384 - 199,2883,4038,5284,3606,2727,1753, 761,1754, 725,1661,1841,4628,3487,3724,5285, # 2400 -5286, 587, 14,3305, 227,2608, 326, 480,2270, 943,2765,3607, 291, 650,1884,5287, # 2416 -1702,1226, 102,1547, 62,3488, 904,4629,3489,1164,4294,5288,5289,1224,1548,2766, # 2432 - 391, 498,1493,5290,1386,1419,5291,2056,1177,4630, 813, 880,1081,2368, 566,1145, # 2448 -4631,2291,1001,1035,2566,2609,2242, 394,1286,5292,5293,2069,5294, 86,1494,1730, # 2464 -4039, 491,1588, 745, 897,2963, 843,3377,4040,2767,2884,3306,1768, 998,2221,2070, # 2480 - 397,1827,1195,1970,3725,3011,3378, 284,5295,3861,2507,2138,2120,1904,5296,4041, # 2496 -2151,4042,4295,1036,3490,1905, 114,2567,4296, 209,1527,5297,5298,2964,2844,2635, # 2512 -2390,2728,3164, 812,2568,5299,3307,5300,1559, 737,1885,3726,1210, 885, 28,2695, # 2528 -3608,3862,5301,4297,1004,1780,4632,5302, 346,1982,2222,2696,4633,3863,1742, 797, # 2544 -1642,4043,1934,1072,1384,2152, 896,4044,3308,3727,3228,2885,3609,5303,2569,1959, # 2560 -4634,2455,1786,5304,5305,5306,4045,4298,1005,1308,3728,4299,2729,4635,4636,1528, # 2576 -2610, 161,1178,4300,1983, 987,4637,1101,4301, 631,4046,1157,3229,2425,1343,1241, # 2592 -1016,2243,2570, 372, 877,2344,2508,1160, 555,1935, 911,4047,5307, 466,1170, 169, # 2608 -1051,2921,2697,3729,2481,3012,1182,2012,2571,1251,2636,5308, 992,2345,3491,1540, # 2624 -2730,1201,2071,2406,1997,2482,5309,4638, 528,1923,2191,1503,1874,1570,2369,3379, # 2640 -3309,5310, 557,1073,5311,1828,3492,2088,2271,3165,3059,3107, 767,3108,2799,4639, # 2656 -1006,4302,4640,2346,1267,2179,3730,3230, 778,4048,3231,2731,1597,2667,5312,4641, # 2672 -5313,3493,5314,5315,5316,3310,2698,1433,3311, 131, 95,1504,4049, 723,4303,3166, # 2688 -1842,3610,2768,2192,4050,2028,2105,3731,5317,3013,4051,1218,5318,3380,3232,4052, # 2704 -4304,2584, 248,1634,3864, 912,5319,2845,3732,3060,3865, 654, 53,5320,3014,5321, # 2720 -1688,4642, 777,3494,1032,4053,1425,5322, 191, 820,2121,2846, 971,4643, 931,3233, # 2736 - 135, 664, 783,3866,1998, 772,2922,1936,4054,3867,4644,2923,3234, 282,2732, 640, # 2752 -1372,3495,1127, 922, 325,3381,5323,5324, 711,2045,5325,5326,4055,2223,2800,1937, # 2768 -4056,3382,2224,2255,3868,2305,5327,4645,3869,1258,3312,4057,3235,2139,2965,4058, # 2784 -4059,5328,2225, 258,3236,4646, 101,1227,5329,3313,1755,5330,1391,3314,5331,2924, # 2800 -2057, 893,5332,5333,5334,1402,4305,2347,5335,5336,3237,3611,5337,5338, 878,1325, # 2816 -1781,2801,4647, 259,1385,2585, 744,1183,2272,4648,5339,4060,2509,5340, 684,1024, # 2832 -4306,5341, 472,3612,3496,1165,3315,4061,4062, 322,2153, 881, 455,1695,1152,1340, # 2848 - 660, 554,2154,4649,1058,4650,4307, 830,1065,3383,4063,4651,1924,5342,1703,1919, # 2864 -5343, 932,2273, 122,5344,4652, 947, 677,5345,3870,2637, 297,1906,1925,2274,4653, # 2880 -2322,3316,5346,5347,4308,5348,4309, 84,4310, 112, 989,5349, 547,1059,4064, 701, # 2896 -3613,1019,5350,4311,5351,3497, 942, 639, 457,2306,2456, 993,2966, 407, 851, 494, # 2912 -4654,3384, 927,5352,1237,5353,2426,3385, 573,4312, 680, 921,2925,1279,1875, 285, # 2928 - 790,1448,1984, 719,2168,5354,5355,4655,4065,4066,1649,5356,1541, 563,5357,1077, # 2944 -5358,3386,3061,3498, 511,3015,4067,4068,3733,4069,1268,2572,3387,3238,4656,4657, # 2960 -5359, 535,1048,1276,1189,2926,2029,3167,1438,1373,2847,2967,1134,2013,5360,4313, # 2976 -1238,2586,3109,1259,5361, 700,5362,2968,3168,3734,4314,5363,4315,1146,1876,1907, # 2992 -4658,2611,4070, 781,2427, 132,1589, 203, 147, 273,2802,2407, 898,1787,2155,4071, # 3008 -4072,5364,3871,2803,5365,5366,4659,4660,5367,3239,5368,1635,3872, 965,5369,1805, # 3024 -2699,1516,3614,1121,1082,1329,3317,4073,1449,3873, 65,1128,2848,2927,2769,1590, # 3040 -3874,5370,5371, 12,2668, 45, 976,2587,3169,4661, 517,2535,1013,1037,3240,5372, # 3056 -3875,2849,5373,3876,5374,3499,5375,2612, 614,1999,2323,3877,3110,2733,2638,5376, # 3072 -2588,4316, 599,1269,5377,1811,3735,5378,2700,3111, 759,1060, 489,1806,3388,3318, # 3088 -1358,5379,5380,2391,1387,1215,2639,2256, 490,5381,5382,4317,1759,2392,2348,5383, # 3104 -4662,3878,1908,4074,2640,1807,3241,4663,3500,3319,2770,2349, 874,5384,5385,3501, # 3120 -3736,1859, 91,2928,3737,3062,3879,4664,5386,3170,4075,2669,5387,3502,1202,1403, # 3136 -3880,2969,2536,1517,2510,4665,3503,2511,5388,4666,5389,2701,1886,1495,1731,4076, # 3152 -2370,4667,5390,2030,5391,5392,4077,2702,1216, 237,2589,4318,2324,4078,3881,4668, # 3168 -4669,2703,3615,3504, 445,4670,5393,5394,5395,5396,2771, 61,4079,3738,1823,4080, # 3184 -5397, 687,2046, 935, 925, 405,2670, 703,1096,1860,2734,4671,4081,1877,1367,2704, # 3200 -3389, 918,2106,1782,2483, 334,3320,1611,1093,4672, 564,3171,3505,3739,3390, 945, # 3216 -2641,2058,4673,5398,1926, 872,4319,5399,3506,2705,3112, 349,4320,3740,4082,4674, # 3232 -3882,4321,3741,2156,4083,4675,4676,4322,4677,2408,2047, 782,4084, 400, 251,4323, # 3248 -1624,5400,5401, 277,3742, 299,1265, 476,1191,3883,2122,4324,4325,1109, 205,5402, # 3264 -2590,1000,2157,3616,1861,5403,5404,5405,4678,5406,4679,2573, 107,2484,2158,4085, # 3280 -3507,3172,5407,1533, 541,1301, 158, 753,4326,2886,3617,5408,1696, 370,1088,4327, # 3296 -4680,3618, 579, 327, 440, 162,2244, 269,1938,1374,3508, 968,3063, 56,1396,3113, # 3312 -2107,3321,3391,5409,1927,2159,4681,3016,5410,3619,5411,5412,3743,4682,2485,5413, # 3328 -2804,5414,1650,4683,5415,2613,5416,5417,4086,2671,3392,1149,3393,4087,3884,4088, # 3344 -5418,1076, 49,5419, 951,3242,3322,3323, 450,2850, 920,5420,1812,2805,2371,4328, # 3360 -1909,1138,2372,3885,3509,5421,3243,4684,1910,1147,1518,2428,4685,3886,5422,4686, # 3376 -2393,2614, 260,1796,3244,5423,5424,3887,3324, 708,5425,3620,1704,5426,3621,1351, # 3392 -1618,3394,3017,1887, 944,4329,3395,4330,3064,3396,4331,5427,3744, 422, 413,1714, # 3408 -3325, 500,2059,2350,4332,2486,5428,1344,1911, 954,5429,1668,5430,5431,4089,2409, # 3424 -4333,3622,3888,4334,5432,2307,1318,2512,3114, 133,3115,2887,4687, 629, 31,2851, # 3440 -2706,3889,4688, 850, 949,4689,4090,2970,1732,2089,4335,1496,1853,5433,4091, 620, # 3456 -3245, 981,1242,3745,3397,1619,3746,1643,3326,2140,2457,1971,1719,3510,2169,5434, # 3472 -3246,5435,5436,3398,1829,5437,1277,4690,1565,2048,5438,1636,3623,3116,5439, 869, # 3488 -2852, 655,3890,3891,3117,4092,3018,3892,1310,3624,4691,5440,5441,5442,1733, 558, # 3504 -4692,3747, 335,1549,3065,1756,4336,3748,1946,3511,1830,1291,1192, 470,2735,2108, # 3520 -2806, 913,1054,4093,5443,1027,5444,3066,4094,4693, 982,2672,3399,3173,3512,3247, # 3536 -3248,1947,2807,5445, 571,4694,5446,1831,5447,3625,2591,1523,2429,5448,2090, 984, # 3552 -4695,3749,1960,5449,3750, 852, 923,2808,3513,3751, 969,1519, 999,2049,2325,1705, # 3568 -5450,3118, 615,1662, 151, 597,4095,2410,2326,1049, 275,4696,3752,4337, 568,3753, # 3584 -3626,2487,4338,3754,5451,2430,2275, 409,3249,5452,1566,2888,3514,1002, 769,2853, # 3600 - 194,2091,3174,3755,2226,3327,4339, 628,1505,5453,5454,1763,2180,3019,4096, 521, # 3616 -1161,2592,1788,2206,2411,4697,4097,1625,4340,4341, 412, 42,3119, 464,5455,2642, # 3632 -4698,3400,1760,1571,2889,3515,2537,1219,2207,3893,2643,2141,2373,4699,4700,3328, # 3648 -1651,3401,3627,5456,5457,3628,2488,3516,5458,3756,5459,5460,2276,2092, 460,5461, # 3664 -4701,5462,3020, 962, 588,3629, 289,3250,2644,1116, 52,5463,3067,1797,5464,5465, # 3680 -5466,1467,5467,1598,1143,3757,4342,1985,1734,1067,4702,1280,3402, 465,4703,1572, # 3696 - 510,5468,1928,2245,1813,1644,3630,5469,4704,3758,5470,5471,2673,1573,1534,5472, # 3712 -5473, 536,1808,1761,3517,3894,3175,2645,5474,5475,5476,4705,3518,2929,1912,2809, # 3728 -5477,3329,1122, 377,3251,5478, 360,5479,5480,4343,1529, 551,5481,2060,3759,1769, # 3744 -2431,5482,2930,4344,3330,3120,2327,2109,2031,4706,1404, 136,1468,1479, 672,1171, # 3760 -3252,2308, 271,3176,5483,2772,5484,2050, 678,2736, 865,1948,4707,5485,2014,4098, # 3776 -2971,5486,2737,2227,1397,3068,3760,4708,4709,1735,2931,3403,3631,5487,3895, 509, # 3792 -2854,2458,2890,3896,5488,5489,3177,3178,4710,4345,2538,4711,2309,1166,1010, 552, # 3808 - 681,1888,5490,5491,2972,2973,4099,1287,1596,1862,3179, 358, 453, 736, 175, 478, # 3824 -1117, 905,1167,1097,5492,1854,1530,5493,1706,5494,2181,3519,2292,3761,3520,3632, # 3840 -4346,2093,4347,5495,3404,1193,2489,4348,1458,2193,2208,1863,1889,1421,3331,2932, # 3856 -3069,2182,3521, 595,2123,5496,4100,5497,5498,4349,1707,2646, 223,3762,1359, 751, # 3872 -3121, 183,3522,5499,2810,3021, 419,2374, 633, 704,3897,2394, 241,5500,5501,5502, # 3888 - 838,3022,3763,2277,2773,2459,3898,1939,2051,4101,1309,3122,2246,1181,5503,1136, # 3904 -2209,3899,2375,1446,4350,2310,4712,5504,5505,4351,1055,2615, 484,3764,5506,4102, # 3920 - 625,4352,2278,3405,1499,4353,4103,5507,4104,4354,3253,2279,2280,3523,5508,5509, # 3936 -2774, 808,2616,3765,3406,4105,4355,3123,2539, 526,3407,3900,4356, 955,5510,1620, # 3952 -4357,2647,2432,5511,1429,3766,1669,1832, 994, 928,5512,3633,1260,5513,5514,5515, # 3968 -1949,2293, 741,2933,1626,4358,2738,2460, 867,1184, 362,3408,1392,5516,5517,4106, # 3984 -4359,1770,1736,3254,2934,4713,4714,1929,2707,1459,1158,5518,3070,3409,2891,1292, # 4000 -1930,2513,2855,3767,1986,1187,2072,2015,2617,4360,5519,2574,2514,2170,3768,2490, # 4016 -3332,5520,3769,4715,5521,5522, 666,1003,3023,1022,3634,4361,5523,4716,1814,2257, # 4032 - 574,3901,1603, 295,1535, 705,3902,4362, 283, 858, 417,5524,5525,3255,4717,4718, # 4048 -3071,1220,1890,1046,2281,2461,4107,1393,1599, 689,2575, 388,4363,5526,2491, 802, # 4064 -5527,2811,3903,2061,1405,2258,5528,4719,3904,2110,1052,1345,3256,1585,5529, 809, # 4080 -5530,5531,5532, 575,2739,3524, 956,1552,1469,1144,2328,5533,2329,1560,2462,3635, # 4096 -3257,4108, 616,2210,4364,3180,2183,2294,5534,1833,5535,3525,4720,5536,1319,3770, # 4112 -3771,1211,3636,1023,3258,1293,2812,5537,5538,5539,3905, 607,2311,3906, 762,2892, # 4128 -1439,4365,1360,4721,1485,3072,5540,4722,1038,4366,1450,2062,2648,4367,1379,4723, # 4144 -2593,5541,5542,4368,1352,1414,2330,2935,1172,5543,5544,3907,3908,4724,1798,1451, # 4160 -5545,5546,5547,5548,2936,4109,4110,2492,2351, 411,4111,4112,3637,3333,3124,4725, # 4176 -1561,2674,1452,4113,1375,5549,5550, 47,2974, 316,5551,1406,1591,2937,3181,5552, # 4192 -1025,2142,3125,3182, 354,2740, 884,2228,4369,2412, 508,3772, 726,3638, 996,2433, # 4208 -3639, 729,5553, 392,2194,1453,4114,4726,3773,5554,5555,2463,3640,2618,1675,2813, # 4224 - 919,2352,2975,2353,1270,4727,4115, 73,5556,5557, 647,5558,3259,2856,2259,1550, # 4240 -1346,3024,5559,1332, 883,3526,5560,5561,5562,5563,3334,2775,5564,1212, 831,1347, # 4256 -4370,4728,2331,3909,1864,3073, 720,3910,4729,4730,3911,5565,4371,5566,5567,4731, # 4272 -5568,5569,1799,4732,3774,2619,4733,3641,1645,2376,4734,5570,2938, 669,2211,2675, # 4288 -2434,5571,2893,5572,5573,1028,3260,5574,4372,2413,5575,2260,1353,5576,5577,4735, # 4304 -3183, 518,5578,4116,5579,4373,1961,5580,2143,4374,5581,5582,3025,2354,2355,3912, # 4320 - 516,1834,1454,4117,2708,4375,4736,2229,2620,1972,1129,3642,5583,2776,5584,2976, # 4336 -1422, 577,1470,3026,1524,3410,5585,5586, 432,4376,3074,3527,5587,2594,1455,2515, # 4352 -2230,1973,1175,5588,1020,2741,4118,3528,4737,5589,2742,5590,1743,1361,3075,3529, # 4368 -2649,4119,4377,4738,2295, 895, 924,4378,2171, 331,2247,3076, 166,1627,3077,1098, # 4384 -5591,1232,2894,2231,3411,4739, 657, 403,1196,2377, 542,3775,3412,1600,4379,3530, # 4400 -5592,4740,2777,3261, 576, 530,1362,4741,4742,2540,2676,3776,4120,5593, 842,3913, # 4416 -5594,2814,2032,1014,4121, 213,2709,3413, 665, 621,4380,5595,3777,2939,2435,5596, # 4432 -2436,3335,3643,3414,4743,4381,2541,4382,4744,3644,1682,4383,3531,1380,5597, 724, # 4448 -2282, 600,1670,5598,1337,1233,4745,3126,2248,5599,1621,4746,5600, 651,4384,5601, # 4464 -1612,4385,2621,5602,2857,5603,2743,2312,3078,5604, 716,2464,3079, 174,1255,2710, # 4480 -4122,3645, 548,1320,1398, 728,4123,1574,5605,1891,1197,3080,4124,5606,3081,3082, # 4496 -3778,3646,3779, 747,5607, 635,4386,4747,5608,5609,5610,4387,5611,5612,4748,5613, # 4512 -3415,4749,2437, 451,5614,3780,2542,2073,4388,2744,4389,4125,5615,1764,4750,5616, # 4528 -4390, 350,4751,2283,2395,2493,5617,4391,4126,2249,1434,4127, 488,4752, 458,4392, # 4544 -4128,3781, 771,1330,2396,3914,2576,3184,2160,2414,1553,2677,3185,4393,5618,2494, # 4560 -2895,2622,1720,2711,4394,3416,4753,5619,2543,4395,5620,3262,4396,2778,5621,2016, # 4576 -2745,5622,1155,1017,3782,3915,5623,3336,2313, 201,1865,4397,1430,5624,4129,5625, # 4592 -5626,5627,5628,5629,4398,1604,5630, 414,1866, 371,2595,4754,4755,3532,2017,3127, # 4608 -4756,1708, 960,4399, 887, 389,2172,1536,1663,1721,5631,2232,4130,2356,2940,1580, # 4624 -5632,5633,1744,4757,2544,4758,4759,5634,4760,5635,2074,5636,4761,3647,3417,2896, # 4640 -4400,5637,4401,2650,3418,2815, 673,2712,2465, 709,3533,4131,3648,4402,5638,1148, # 4656 - 502, 634,5639,5640,1204,4762,3649,1575,4763,2623,3783,5641,3784,3128, 948,3263, # 4672 - 121,1745,3916,1110,5642,4403,3083,2516,3027,4132,3785,1151,1771,3917,1488,4133, # 4688 -1987,5643,2438,3534,5644,5645,2094,5646,4404,3918,1213,1407,2816, 531,2746,2545, # 4704 -3264,1011,1537,4764,2779,4405,3129,1061,5647,3786,3787,1867,2897,5648,2018, 120, # 4720 -4406,4407,2063,3650,3265,2314,3919,2678,3419,1955,4765,4134,5649,3535,1047,2713, # 4736 -1266,5650,1368,4766,2858, 649,3420,3920,2546,2747,1102,2859,2679,5651,5652,2000, # 4752 -5653,1111,3651,2977,5654,2495,3921,3652,2817,1855,3421,3788,5655,5656,3422,2415, # 4768 -2898,3337,3266,3653,5657,2577,5658,3654,2818,4135,1460, 856,5659,3655,5660,2899, # 4784 -2978,5661,2900,3922,5662,4408, 632,2517, 875,3923,1697,3924,2296,5663,5664,4767, # 4800 -3028,1239, 580,4768,4409,5665, 914, 936,2075,1190,4136,1039,2124,5666,5667,5668, # 4816 -5669,3423,1473,5670,1354,4410,3925,4769,2173,3084,4137, 915,3338,4411,4412,3339, # 4832 -1605,1835,5671,2748, 398,3656,4413,3926,4138, 328,1913,2860,4139,3927,1331,4414, # 4848 -3029, 937,4415,5672,3657,4140,4141,3424,2161,4770,3425, 524, 742, 538,3085,1012, # 4864 -5673,5674,3928,2466,5675, 658,1103, 225,3929,5676,5677,4771,5678,4772,5679,3267, # 4880 -1243,5680,4142, 963,2250,4773,5681,2714,3658,3186,5682,5683,2596,2332,5684,4774, # 4896 -5685,5686,5687,3536, 957,3426,2547,2033,1931,2941,2467, 870,2019,3659,1746,2780, # 4912 -2781,2439,2468,5688,3930,5689,3789,3130,3790,3537,3427,3791,5690,1179,3086,5691, # 4928 -3187,2378,4416,3792,2548,3188,3131,2749,4143,5692,3428,1556,2549,2297, 977,2901, # 4944 -2034,4144,1205,3429,5693,1765,3430,3189,2125,1271, 714,1689,4775,3538,5694,2333, # 4960 -3931, 533,4417,3660,2184, 617,5695,2469,3340,3539,2315,5696,5697,3190,5698,5699, # 4976 -3932,1988, 618, 427,2651,3540,3431,5700,5701,1244,1690,5702,2819,4418,4776,5703, # 4992 -3541,4777,5704,2284,1576, 473,3661,4419,3432, 972,5705,3662,5706,3087,5707,5708, # 5008 -4778,4779,5709,3793,4145,4146,5710, 153,4780, 356,5711,1892,2902,4420,2144, 408, # 5024 - 803,2357,5712,3933,5713,4421,1646,2578,2518,4781,4782,3934,5714,3935,4422,5715, # 5040 -2416,3433, 752,5716,5717,1962,3341,2979,5718, 746,3030,2470,4783,4423,3794, 698, # 5056 -4784,1893,4424,3663,2550,4785,3664,3936,5719,3191,3434,5720,1824,1302,4147,2715, # 5072 -3937,1974,4425,5721,4426,3192, 823,1303,1288,1236,2861,3542,4148,3435, 774,3938, # 5088 -5722,1581,4786,1304,2862,3939,4787,5723,2440,2162,1083,3268,4427,4149,4428, 344, # 5104 -1173, 288,2316, 454,1683,5724,5725,1461,4788,4150,2597,5726,5727,4789, 985, 894, # 5120 -5728,3436,3193,5729,1914,2942,3795,1989,5730,2111,1975,5731,4151,5732,2579,1194, # 5136 - 425,5733,4790,3194,1245,3796,4429,5734,5735,2863,5736, 636,4791,1856,3940, 760, # 5152 -1800,5737,4430,2212,1508,4792,4152,1894,1684,2298,5738,5739,4793,4431,4432,2213, # 5168 - 479,5740,5741, 832,5742,4153,2496,5743,2980,2497,3797, 990,3132, 627,1815,2652, # 5184 -4433,1582,4434,2126,2112,3543,4794,5744, 799,4435,3195,5745,4795,2113,1737,3031, # 5200 -1018, 543, 754,4436,3342,1676,4796,4797,4154,4798,1489,5746,3544,5747,2624,2903, # 5216 -4155,5748,5749,2981,5750,5751,5752,5753,3196,4799,4800,2185,1722,5754,3269,3270, # 5232 -1843,3665,1715, 481, 365,1976,1857,5755,5756,1963,2498,4801,5757,2127,3666,3271, # 5248 - 433,1895,2064,2076,5758, 602,2750,5759,5760,5761,5762,5763,3032,1628,3437,5764, # 5264 -3197,4802,4156,2904,4803,2519,5765,2551,2782,5766,5767,5768,3343,4804,2905,5769, # 5280 -4805,5770,2864,4806,4807,1221,2982,4157,2520,5771,5772,5773,1868,1990,5774,5775, # 5296 -5776,1896,5777,5778,4808,1897,4158, 318,5779,2095,4159,4437,5780,5781, 485,5782, # 5312 - 938,3941, 553,2680, 116,5783,3942,3667,5784,3545,2681,2783,3438,3344,2820,5785, # 5328 -3668,2943,4160,1747,2944,2983,5786,5787, 207,5788,4809,5789,4810,2521,5790,3033, # 5344 - 890,3669,3943,5791,1878,3798,3439,5792,2186,2358,3440,1652,5793,5794,5795, 941, # 5360 -2299, 208,3546,4161,2020, 330,4438,3944,2906,2499,3799,4439,4811,5796,5797,5798, # 5376 -) -# fmt: on diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/big5prober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/big5prober.py deleted file mode 100644 index ef09c60..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/big5prober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .chardistribution import Big5DistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import BIG5_SM_MODEL - - -class Big5Prober(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(BIG5_SM_MODEL) - self.distribution_analyzer = Big5DistributionAnalysis() - self.reset() - - @property - def charset_name(self) -> str: - return "Big5" - - @property - def language(self) -> str: - return "Chinese" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/chardistribution.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/chardistribution.py deleted file mode 100644 index 176cb99..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/chardistribution.py +++ /dev/null @@ -1,261 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Tuple, Union - -from .big5freq import ( - BIG5_CHAR_TO_FREQ_ORDER, - BIG5_TABLE_SIZE, - BIG5_TYPICAL_DISTRIBUTION_RATIO, -) -from .euckrfreq import ( - EUCKR_CHAR_TO_FREQ_ORDER, - EUCKR_TABLE_SIZE, - EUCKR_TYPICAL_DISTRIBUTION_RATIO, -) -from .euctwfreq import ( - EUCTW_CHAR_TO_FREQ_ORDER, - EUCTW_TABLE_SIZE, - EUCTW_TYPICAL_DISTRIBUTION_RATIO, -) -from .gb2312freq import ( - GB2312_CHAR_TO_FREQ_ORDER, - GB2312_TABLE_SIZE, - GB2312_TYPICAL_DISTRIBUTION_RATIO, -) -from .jisfreq import ( - JIS_CHAR_TO_FREQ_ORDER, - JIS_TABLE_SIZE, - JIS_TYPICAL_DISTRIBUTION_RATIO, -) -from .johabfreq import JOHAB_TO_EUCKR_ORDER_TABLE - - -class CharDistributionAnalysis: - ENOUGH_DATA_THRESHOLD = 1024 - SURE_YES = 0.99 - SURE_NO = 0.01 - MINIMUM_DATA_THRESHOLD = 3 - - def __init__(self) -> None: - # Mapping table to get frequency order from char order (get from - # GetOrder()) - self._char_to_freq_order: Tuple[int, ...] = tuple() - self._table_size = 0 # Size of above table - # This is a constant value which varies from language to language, - # used in calculating confidence. See - # http://www.mozilla.org/projects/intl/UniversalCharsetDetection.html - # for further detail. - self.typical_distribution_ratio = 0.0 - self._done = False - self._total_chars = 0 - self._freq_chars = 0 - self.reset() - - def reset(self) -> None: - """reset analyser, clear any state""" - # If this flag is set to True, detection is done and conclusion has - # been made - self._done = False - self._total_chars = 0 # Total characters encountered - # The number of characters whose frequency order is less than 512 - self._freq_chars = 0 - - def feed(self, char: Union[bytes, bytearray], char_len: int) -> None: - """feed a character with known length""" - if char_len == 2: - # we only care about 2-bytes character in our distribution analysis - order = self.get_order(char) - else: - order = -1 - if order >= 0: - self._total_chars += 1 - # order is valid - if order < self._table_size: - if 512 > self._char_to_freq_order[order]: - self._freq_chars += 1 - - def get_confidence(self) -> float: - """return confidence based on existing data""" - # if we didn't receive any character in our consideration range, - # return negative answer - if self._total_chars <= 0 or self._freq_chars <= self.MINIMUM_DATA_THRESHOLD: - return self.SURE_NO - - if self._total_chars != self._freq_chars: - r = self._freq_chars / ( - (self._total_chars - self._freq_chars) * self.typical_distribution_ratio - ) - if r < self.SURE_YES: - return r - - # normalize confidence (we don't want to be 100% sure) - return self.SURE_YES - - def got_enough_data(self) -> bool: - # It is not necessary to receive all data to draw conclusion. - # For charset detection, certain amount of data is enough - return self._total_chars > self.ENOUGH_DATA_THRESHOLD - - def get_order(self, _: Union[bytes, bytearray]) -> int: - # We do not handle characters based on the original encoding string, - # but convert this encoding string to a number, here called order. - # This allows multiple encodings of a language to share one frequency - # table. - return -1 - - -class EUCTWDistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = EUCTW_CHAR_TO_FREQ_ORDER - self._table_size = EUCTW_TABLE_SIZE - self.typical_distribution_ratio = EUCTW_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - # for euc-TW encoding, we are interested - # first byte range: 0xc4 -- 0xfe - # second byte range: 0xa1 -- 0xfe - # no validation needed here. State machine has done that - first_char = byte_str[0] - if first_char >= 0xC4: - return 94 * (first_char - 0xC4) + byte_str[1] - 0xA1 - return -1 - - -class EUCKRDistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER - self._table_size = EUCKR_TABLE_SIZE - self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - # for euc-KR encoding, we are interested - # first byte range: 0xb0 -- 0xfe - # second byte range: 0xa1 -- 0xfe - # no validation needed here. State machine has done that - first_char = byte_str[0] - if first_char >= 0xB0: - return 94 * (first_char - 0xB0) + byte_str[1] - 0xA1 - return -1 - - -class JOHABDistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = EUCKR_CHAR_TO_FREQ_ORDER - self._table_size = EUCKR_TABLE_SIZE - self.typical_distribution_ratio = EUCKR_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - first_char = byte_str[0] - if 0x88 <= first_char < 0xD4: - code = first_char * 256 + byte_str[1] - return JOHAB_TO_EUCKR_ORDER_TABLE.get(code, -1) - return -1 - - -class GB2312DistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = GB2312_CHAR_TO_FREQ_ORDER - self._table_size = GB2312_TABLE_SIZE - self.typical_distribution_ratio = GB2312_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - # for GB2312 encoding, we are interested - # first byte range: 0xb0 -- 0xfe - # second byte range: 0xa1 -- 0xfe - # no validation needed here. State machine has done that - first_char, second_char = byte_str[0], byte_str[1] - if (first_char >= 0xB0) and (second_char >= 0xA1): - return 94 * (first_char - 0xB0) + second_char - 0xA1 - return -1 - - -class Big5DistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = BIG5_CHAR_TO_FREQ_ORDER - self._table_size = BIG5_TABLE_SIZE - self.typical_distribution_ratio = BIG5_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - # for big5 encoding, we are interested - # first byte range: 0xa4 -- 0xfe - # second byte range: 0x40 -- 0x7e , 0xa1 -- 0xfe - # no validation needed here. State machine has done that - first_char, second_char = byte_str[0], byte_str[1] - if first_char >= 0xA4: - if second_char >= 0xA1: - return 157 * (first_char - 0xA4) + second_char - 0xA1 + 63 - return 157 * (first_char - 0xA4) + second_char - 0x40 - return -1 - - -class SJISDistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER - self._table_size = JIS_TABLE_SIZE - self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - # for sjis encoding, we are interested - # first byte range: 0x81 -- 0x9f , 0xe0 -- 0xfe - # second byte range: 0x40 -- 0x7e, 0x81 -- oxfe - # no validation needed here. State machine has done that - first_char, second_char = byte_str[0], byte_str[1] - if 0x81 <= first_char <= 0x9F: - order = 188 * (first_char - 0x81) - elif 0xE0 <= first_char <= 0xEF: - order = 188 * (first_char - 0xE0 + 31) - else: - return -1 - order = order + second_char - 0x40 - if second_char > 0x7F: - order = -1 - return order - - -class EUCJPDistributionAnalysis(CharDistributionAnalysis): - def __init__(self) -> None: - super().__init__() - self._char_to_freq_order = JIS_CHAR_TO_FREQ_ORDER - self._table_size = JIS_TABLE_SIZE - self.typical_distribution_ratio = JIS_TYPICAL_DISTRIBUTION_RATIO - - def get_order(self, byte_str: Union[bytes, bytearray]) -> int: - # for euc-JP encoding, we are interested - # first byte range: 0xa0 -- 0xfe - # second byte range: 0xa1 -- 0xfe - # no validation needed here. State machine has done that - char = byte_str[0] - if char >= 0xA0: - return 94 * (char - 0xA1) + byte_str[1] - 0xA1 - return -1 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/charsetgroupprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/charsetgroupprober.py deleted file mode 100644 index 6def56b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/charsetgroupprober.py +++ /dev/null @@ -1,106 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import List, Optional, Union - -from .charsetprober import CharSetProber -from .enums import LanguageFilter, ProbingState - - -class CharSetGroupProber(CharSetProber): - def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: - super().__init__(lang_filter=lang_filter) - self._active_num = 0 - self.probers: List[CharSetProber] = [] - self._best_guess_prober: Optional[CharSetProber] = None - - def reset(self) -> None: - super().reset() - self._active_num = 0 - for prober in self.probers: - prober.reset() - prober.active = True - self._active_num += 1 - self._best_guess_prober = None - - @property - def charset_name(self) -> Optional[str]: - if not self._best_guess_prober: - self.get_confidence() - if not self._best_guess_prober: - return None - return self._best_guess_prober.charset_name - - @property - def language(self) -> Optional[str]: - if not self._best_guess_prober: - self.get_confidence() - if not self._best_guess_prober: - return None - return self._best_guess_prober.language - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - for prober in self.probers: - if not prober.active: - continue - state = prober.feed(byte_str) - if not state: - continue - if state == ProbingState.FOUND_IT: - self._best_guess_prober = prober - self._state = ProbingState.FOUND_IT - return self.state - if state == ProbingState.NOT_ME: - prober.active = False - self._active_num -= 1 - if self._active_num <= 0: - self._state = ProbingState.NOT_ME - return self.state - return self.state - - def get_confidence(self) -> float: - state = self.state - if state == ProbingState.FOUND_IT: - return 0.99 - if state == ProbingState.NOT_ME: - return 0.01 - best_conf = 0.0 - self._best_guess_prober = None - for prober in self.probers: - if not prober.active: - self.logger.debug("%s not active", prober.charset_name) - continue - conf = prober.get_confidence() - self.logger.debug( - "%s %s confidence = %s", prober.charset_name, prober.language, conf - ) - if best_conf < conf: - best_conf = conf - self._best_guess_prober = prober - if not self._best_guess_prober: - return 0.0 - return best_conf diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/charsetprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/charsetprober.py deleted file mode 100644 index a103ca1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/charsetprober.py +++ /dev/null @@ -1,147 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -import logging -import re -from typing import Optional, Union - -from .enums import LanguageFilter, ProbingState - -INTERNATIONAL_WORDS_PATTERN = re.compile( - b"[a-zA-Z]*[\x80-\xFF]+[a-zA-Z]*[^a-zA-Z\x80-\xFF]?" -) - - -class CharSetProber: - - SHORTCUT_THRESHOLD = 0.95 - - def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: - self._state = ProbingState.DETECTING - self.active = True - self.lang_filter = lang_filter - self.logger = logging.getLogger(__name__) - - def reset(self) -> None: - self._state = ProbingState.DETECTING - - @property - def charset_name(self) -> Optional[str]: - return None - - @property - def language(self) -> Optional[str]: - raise NotImplementedError - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - raise NotImplementedError - - @property - def state(self) -> ProbingState: - return self._state - - def get_confidence(self) -> float: - return 0.0 - - @staticmethod - def filter_high_byte_only(buf: Union[bytes, bytearray]) -> bytes: - buf = re.sub(b"([\x00-\x7F])+", b" ", buf) - return buf - - @staticmethod - def filter_international_words(buf: Union[bytes, bytearray]) -> bytearray: - """ - We define three types of bytes: - alphabet: english alphabets [a-zA-Z] - international: international characters [\x80-\xFF] - marker: everything else [^a-zA-Z\x80-\xFF] - The input buffer can be thought to contain a series of words delimited - by markers. This function works to filter all words that contain at - least one international character. All contiguous sequences of markers - are replaced by a single space ascii character. - This filter applies to all scripts which do not use English characters. - """ - filtered = bytearray() - - # This regex expression filters out only words that have at-least one - # international character. The word may include one marker character at - # the end. - words = INTERNATIONAL_WORDS_PATTERN.findall(buf) - - for word in words: - filtered.extend(word[:-1]) - - # If the last character in the word is a marker, replace it with a - # space as markers shouldn't affect our analysis (they are used - # similarly across all languages and may thus have similar - # frequencies). - last_char = word[-1:] - if not last_char.isalpha() and last_char < b"\x80": - last_char = b" " - filtered.extend(last_char) - - return filtered - - @staticmethod - def remove_xml_tags(buf: Union[bytes, bytearray]) -> bytes: - """ - Returns a copy of ``buf`` that retains only the sequences of English - alphabet and high byte characters that are not between <> characters. - This filter can be applied to all scripts which contain both English - characters and extended ASCII characters, but is currently only used by - ``Latin1Prober``. - """ - filtered = bytearray() - in_tag = False - prev = 0 - buf = memoryview(buf).cast("c") - - for curr, buf_char in enumerate(buf): - # Check if we're coming out of or entering an XML tag - - # https://github.com/python/typeshed/issues/8182 - if buf_char == b">": # type: ignore[comparison-overlap] - prev = curr + 1 - in_tag = False - # https://github.com/python/typeshed/issues/8182 - elif buf_char == b"<": # type: ignore[comparison-overlap] - if curr > prev and not in_tag: - # Keep everything after last non-extended-ASCII, - # non-alphabetic character - filtered.extend(buf[prev:curr]) - # Output a space to delimit stretch we kept - filtered.extend(b" ") - in_tag = True - - # If we're not in a tag... - if not in_tag: - # Keep everything after last non-extended-ASCII, non-alphabetic - # character - filtered.extend(buf[prev:]) - - return filtered diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cli/__init__.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cli/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cli/chardetect.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cli/chardetect.py deleted file mode 100644 index 43f6e14..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cli/chardetect.py +++ /dev/null @@ -1,112 +0,0 @@ -""" -Script which takes one or more file paths and reports on their detected -encodings - -Example:: - - % chardetect somefile someotherfile - somefile: windows-1252 with confidence 0.5 - someotherfile: ascii with confidence 1.0 - -If no paths are provided, it takes its input from stdin. - -""" - - -import argparse -import sys -from typing import Iterable, List, Optional - -from .. import __version__ -from ..universaldetector import UniversalDetector - - -def description_of( - lines: Iterable[bytes], - name: str = "stdin", - minimal: bool = False, - should_rename_legacy: bool = False, -) -> Optional[str]: - """ - Return a string describing the probable encoding of a file or - list of strings. - - :param lines: The lines to get the encoding of. - :type lines: Iterable of bytes - :param name: Name of file or collection of lines - :type name: str - :param should_rename_legacy: Should we rename legacy encodings to - their more modern equivalents? - :type should_rename_legacy: ``bool`` - """ - u = UniversalDetector(should_rename_legacy=should_rename_legacy) - for line in lines: - line = bytearray(line) - u.feed(line) - # shortcut out of the loop to save reading further - particularly useful if we read a BOM. - if u.done: - break - u.close() - result = u.result - if minimal: - return result["encoding"] - if result["encoding"]: - return f'{name}: {result["encoding"]} with confidence {result["confidence"]}' - return f"{name}: no result" - - -def main(argv: Optional[List[str]] = None) -> None: - """ - Handles command line arguments and gets things started. - - :param argv: List of arguments, as if specified on the command-line. - If None, ``sys.argv[1:]`` is used instead. - :type argv: list of str - """ - # Get command line arguments - parser = argparse.ArgumentParser( - description=( - "Takes one or more file paths and reports their detected encodings" - ) - ) - parser.add_argument( - "input", - help="File whose encoding we would like to determine. (default: stdin)", - type=argparse.FileType("rb"), - nargs="*", - default=[sys.stdin.buffer], - ) - parser.add_argument( - "--minimal", - help="Print only the encoding to standard output", - action="store_true", - ) - parser.add_argument( - "-l", - "--legacy", - help="Rename legacy encodings to more modern ones.", - action="store_true", - ) - parser.add_argument( - "--version", action="version", version=f"%(prog)s {__version__}" - ) - args = parser.parse_args(argv) - - for f in args.input: - if f.isatty(): - print( - "You are running chardetect interactively. Press " - "CTRL-D twice at the start of a blank line to signal the " - "end of your input. If you want help, run chardetect " - "--help\n", - file=sys.stderr, - ) - print( - description_of( - f, f.name, minimal=args.minimal, should_rename_legacy=args.legacy - ) - ) - - -if __name__ == "__main__": - main() diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/codingstatemachine.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/codingstatemachine.py deleted file mode 100644 index 8ed4a87..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/codingstatemachine.py +++ /dev/null @@ -1,90 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -import logging - -from .codingstatemachinedict import CodingStateMachineDict -from .enums import MachineState - - -class CodingStateMachine: - """ - A state machine to verify a byte sequence for a particular encoding. For - each byte the detector receives, it will feed that byte to every active - state machine available, one byte at a time. The state machine changes its - state based on its previous state and the byte it receives. There are 3 - states in a state machine that are of interest to an auto-detector: - - START state: This is the state to start with, or a legal byte sequence - (i.e. a valid code point) for character has been identified. - - ME state: This indicates that the state machine identified a byte sequence - that is specific to the charset it is designed for and that - there is no other possible encoding which can contain this byte - sequence. This will to lead to an immediate positive answer for - the detector. - - ERROR state: This indicates the state machine identified an illegal byte - sequence for that encoding. This will lead to an immediate - negative answer for this encoding. Detector will exclude this - encoding from consideration from here on. - """ - - def __init__(self, sm: CodingStateMachineDict) -> None: - self._model = sm - self._curr_byte_pos = 0 - self._curr_char_len = 0 - self._curr_state = MachineState.START - self.active = True - self.logger = logging.getLogger(__name__) - self.reset() - - def reset(self) -> None: - self._curr_state = MachineState.START - - def next_state(self, c: int) -> int: - # for each byte we get its class - # if it is first byte, we also get byte length - byte_class = self._model["class_table"][c] - if self._curr_state == MachineState.START: - self._curr_byte_pos = 0 - self._curr_char_len = self._model["char_len_table"][byte_class] - # from byte's class and state_table, we get its next state - curr_state = self._curr_state * self._model["class_factor"] + byte_class - self._curr_state = self._model["state_table"][curr_state] - self._curr_byte_pos += 1 - return self._curr_state - - def get_current_charlen(self) -> int: - return self._curr_char_len - - def get_coding_state_machine(self) -> str: - return self._model["name"] - - @property - def language(self) -> str: - return self._model["language"] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/codingstatemachinedict.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/codingstatemachinedict.py deleted file mode 100644 index 7a3c4c7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/codingstatemachinedict.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import TYPE_CHECKING, Tuple - -if TYPE_CHECKING: - # TypedDict was introduced in Python 3.8. - # - # TODO: Remove the else block and TYPE_CHECKING check when dropping support - # for Python 3.7. - from typing import TypedDict - - class CodingStateMachineDict(TypedDict, total=False): - class_table: Tuple[int, ...] - class_factor: int - state_table: Tuple[int, ...] - char_len_table: Tuple[int, ...] - name: str - language: str # Optional key - -else: - CodingStateMachineDict = dict diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cp949prober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cp949prober.py deleted file mode 100644 index fa7307e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/cp949prober.py +++ /dev/null @@ -1,49 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .chardistribution import EUCKRDistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import CP949_SM_MODEL - - -class CP949Prober(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(CP949_SM_MODEL) - # NOTE: CP949 is a superset of EUC-KR, so the distribution should be - # not different. - self.distribution_analyzer = EUCKRDistributionAnalysis() - self.reset() - - @property - def charset_name(self) -> str: - return "CP949" - - @property - def language(self) -> str: - return "Korean" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/enums.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/enums.py deleted file mode 100644 index 5e3e198..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/enums.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -All of the Enums that are used throughout the chardet package. - -:author: Dan Blanchard (dan.blanchard@gmail.com) -""" - -from enum import Enum, Flag - - -class InputState: - """ - This enum represents the different states a universal detector can be in. - """ - - PURE_ASCII = 0 - ESC_ASCII = 1 - HIGH_BYTE = 2 - - -class LanguageFilter(Flag): - """ - This enum represents the different language filters we can apply to a - ``UniversalDetector``. - """ - - NONE = 0x00 - CHINESE_SIMPLIFIED = 0x01 - CHINESE_TRADITIONAL = 0x02 - JAPANESE = 0x04 - KOREAN = 0x08 - NON_CJK = 0x10 - ALL = 0x1F - CHINESE = CHINESE_SIMPLIFIED | CHINESE_TRADITIONAL - CJK = CHINESE | JAPANESE | KOREAN - - -class ProbingState(Enum): - """ - This enum represents the different states a prober can be in. - """ - - DETECTING = 0 - FOUND_IT = 1 - NOT_ME = 2 - - -class MachineState: - """ - This enum represents the different states a state machine can be in. - """ - - START = 0 - ERROR = 1 - ITS_ME = 2 - - -class SequenceLikelihood: - """ - This enum represents the likelihood of a character following the previous one. - """ - - NEGATIVE = 0 - UNLIKELY = 1 - LIKELY = 2 - POSITIVE = 3 - - @classmethod - def get_num_categories(cls) -> int: - """:returns: The number of likelihood categories in the enum.""" - return 4 - - -class CharacterCategory: - """ - This enum represents the different categories language models for - ``SingleByteCharsetProber`` put characters into. - - Anything less than CONTROL is considered a letter. - """ - - UNDEFINED = 255 - LINE_BREAK = 254 - SYMBOL = 253 - DIGIT = 252 - CONTROL = 251 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/escprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/escprober.py deleted file mode 100644 index fd71383..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/escprober.py +++ /dev/null @@ -1,102 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Optional, Union - -from .charsetprober import CharSetProber -from .codingstatemachine import CodingStateMachine -from .enums import LanguageFilter, MachineState, ProbingState -from .escsm import ( - HZ_SM_MODEL, - ISO2022CN_SM_MODEL, - ISO2022JP_SM_MODEL, - ISO2022KR_SM_MODEL, -) - - -class EscCharSetProber(CharSetProber): - """ - This CharSetProber uses a "code scheme" approach for detecting encodings, - whereby easily recognizable escape or shift sequences are relied on to - identify these encodings. - """ - - def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: - super().__init__(lang_filter=lang_filter) - self.coding_sm = [] - if self.lang_filter & LanguageFilter.CHINESE_SIMPLIFIED: - self.coding_sm.append(CodingStateMachine(HZ_SM_MODEL)) - self.coding_sm.append(CodingStateMachine(ISO2022CN_SM_MODEL)) - if self.lang_filter & LanguageFilter.JAPANESE: - self.coding_sm.append(CodingStateMachine(ISO2022JP_SM_MODEL)) - if self.lang_filter & LanguageFilter.KOREAN: - self.coding_sm.append(CodingStateMachine(ISO2022KR_SM_MODEL)) - self.active_sm_count = 0 - self._detected_charset: Optional[str] = None - self._detected_language: Optional[str] = None - self._state = ProbingState.DETECTING - self.reset() - - def reset(self) -> None: - super().reset() - for coding_sm in self.coding_sm: - coding_sm.active = True - coding_sm.reset() - self.active_sm_count = len(self.coding_sm) - self._detected_charset = None - self._detected_language = None - - @property - def charset_name(self) -> Optional[str]: - return self._detected_charset - - @property - def language(self) -> Optional[str]: - return self._detected_language - - def get_confidence(self) -> float: - return 0.99 if self._detected_charset else 0.00 - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - for c in byte_str: - for coding_sm in self.coding_sm: - if not coding_sm.active: - continue - coding_state = coding_sm.next_state(c) - if coding_state == MachineState.ERROR: - coding_sm.active = False - self.active_sm_count -= 1 - if self.active_sm_count <= 0: - self._state = ProbingState.NOT_ME - return self.state - elif coding_state == MachineState.ITS_ME: - self._state = ProbingState.FOUND_IT - self._detected_charset = coding_sm.get_coding_state_machine() - self._detected_language = coding_sm.language - return self.state - - return self.state diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/escsm.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/escsm.py deleted file mode 100644 index 11d4adf..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/escsm.py +++ /dev/null @@ -1,261 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .codingstatemachinedict import CodingStateMachineDict -from .enums import MachineState - -# fmt: off -HZ_CLS = ( - 1, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 - 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f - 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 - 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f - 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 - 0, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f - 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 - 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f - 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 - 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f - 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 - 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f - 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 - 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f - 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 - 0, 0, 0, 4, 0, 5, 2, 0, # 78 - 7f - 1, 1, 1, 1, 1, 1, 1, 1, # 80 - 87 - 1, 1, 1, 1, 1, 1, 1, 1, # 88 - 8f - 1, 1, 1, 1, 1, 1, 1, 1, # 90 - 97 - 1, 1, 1, 1, 1, 1, 1, 1, # 98 - 9f - 1, 1, 1, 1, 1, 1, 1, 1, # a0 - a7 - 1, 1, 1, 1, 1, 1, 1, 1, # a8 - af - 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7 - 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf - 1, 1, 1, 1, 1, 1, 1, 1, # c0 - c7 - 1, 1, 1, 1, 1, 1, 1, 1, # c8 - cf - 1, 1, 1, 1, 1, 1, 1, 1, # d0 - d7 - 1, 1, 1, 1, 1, 1, 1, 1, # d8 - df - 1, 1, 1, 1, 1, 1, 1, 1, # e0 - e7 - 1, 1, 1, 1, 1, 1, 1, 1, # e8 - ef - 1, 1, 1, 1, 1, 1, 1, 1, # f0 - f7 - 1, 1, 1, 1, 1, 1, 1, 1, # f8 - ff -) - -HZ_ST = ( -MachineState.START, MachineState.ERROR, 3, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07 -MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f -MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.START, MachineState.START, 4, MachineState.ERROR, # 10-17 - 5, MachineState.ERROR, 6, MachineState.ERROR, 5, 5, 4, MachineState.ERROR, # 18-1f - 4, MachineState.ERROR, 4, 4, 4, MachineState.ERROR, 4, MachineState.ERROR, # 20-27 - 4, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 28-2f -) -# fmt: on - -HZ_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) - -HZ_SM_MODEL: CodingStateMachineDict = { - "class_table": HZ_CLS, - "class_factor": 6, - "state_table": HZ_ST, - "char_len_table": HZ_CHAR_LEN_TABLE, - "name": "HZ-GB-2312", - "language": "Chinese", -} - -# fmt: off -ISO2022CN_CLS = ( - 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 - 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f - 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 - 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f - 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 - 0, 3, 0, 0, 0, 0, 0, 0, # 28 - 2f - 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 - 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f - 0, 0, 0, 4, 0, 0, 0, 0, # 40 - 47 - 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f - 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 - 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f - 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 - 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f - 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 - 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f - 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 - 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f - 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 - 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f - 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 - 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af - 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 - 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf - 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 - 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf - 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 - 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df - 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 - 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef - 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 - 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff -) - -ISO2022CN_ST = ( - MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07 - MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f - MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17 - MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, # 18-1f - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 20-27 - 5, 6, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 28-2f - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 30-37 - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, # 38-3f -) -# fmt: on - -ISO2022CN_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0) - -ISO2022CN_SM_MODEL: CodingStateMachineDict = { - "class_table": ISO2022CN_CLS, - "class_factor": 9, - "state_table": ISO2022CN_ST, - "char_len_table": ISO2022CN_CHAR_LEN_TABLE, - "name": "ISO-2022-CN", - "language": "Chinese", -} - -# fmt: off -ISO2022JP_CLS = ( - 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 - 0, 0, 0, 0, 0, 0, 2, 2, # 08 - 0f - 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 - 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f - 0, 0, 0, 0, 7, 0, 0, 0, # 20 - 27 - 3, 0, 0, 0, 0, 0, 0, 0, # 28 - 2f - 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 - 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f - 6, 0, 4, 0, 8, 0, 0, 0, # 40 - 47 - 0, 9, 5, 0, 0, 0, 0, 0, # 48 - 4f - 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 - 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f - 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 - 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f - 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 - 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f - 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 - 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f - 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 - 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f - 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 - 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af - 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 - 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf - 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 - 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf - 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 - 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df - 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 - 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef - 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 - 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff -) - -ISO2022JP_ST = ( - MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 00-07 - MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 08-0f - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 10-17 - MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, # 18-1f - MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 20-27 - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 6, MachineState.ITS_ME, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, # 28-2f - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, # 30-37 - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 38-3f - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ERROR, MachineState.START, MachineState.START, # 40-47 -) -# fmt: on - -ISO2022JP_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - -ISO2022JP_SM_MODEL: CodingStateMachineDict = { - "class_table": ISO2022JP_CLS, - "class_factor": 10, - "state_table": ISO2022JP_ST, - "char_len_table": ISO2022JP_CHAR_LEN_TABLE, - "name": "ISO-2022-JP", - "language": "Japanese", -} - -# fmt: off -ISO2022KR_CLS = ( - 2, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 - 0, 0, 0, 0, 0, 0, 0, 0, # 08 - 0f - 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 - 0, 0, 0, 1, 0, 0, 0, 0, # 18 - 1f - 0, 0, 0, 0, 3, 0, 0, 0, # 20 - 27 - 0, 4, 0, 0, 0, 0, 0, 0, # 28 - 2f - 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 - 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f - 0, 0, 0, 5, 0, 0, 0, 0, # 40 - 47 - 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f - 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 - 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f - 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 - 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f - 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 - 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f - 2, 2, 2, 2, 2, 2, 2, 2, # 80 - 87 - 2, 2, 2, 2, 2, 2, 2, 2, # 88 - 8f - 2, 2, 2, 2, 2, 2, 2, 2, # 90 - 97 - 2, 2, 2, 2, 2, 2, 2, 2, # 98 - 9f - 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 - 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af - 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 - 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf - 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 - 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf - 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 - 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df - 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 - 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef - 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 - 2, 2, 2, 2, 2, 2, 2, 2, # f8 - ff -) - -ISO2022KR_ST = ( - MachineState.START, 3, MachineState.ERROR, MachineState.START, MachineState.START, MachineState.START, MachineState.ERROR, MachineState.ERROR, # 00-07 - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ITS_ME, # 08-0f - MachineState.ITS_ME, MachineState.ITS_ME, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 4, MachineState.ERROR, MachineState.ERROR, # 10-17 - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, 5, MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, # 18-1f - MachineState.ERROR, MachineState.ERROR, MachineState.ERROR, MachineState.ITS_ME, MachineState.START, MachineState.START, MachineState.START, MachineState.START, # 20-27 -) -# fmt: on - -ISO2022KR_CHAR_LEN_TABLE = (0, 0, 0, 0, 0, 0) - -ISO2022KR_SM_MODEL: CodingStateMachineDict = { - "class_table": ISO2022KR_CLS, - "class_factor": 6, - "state_table": ISO2022KR_ST, - "char_len_table": ISO2022KR_CHAR_LEN_TABLE, - "name": "ISO-2022-KR", - "language": "Korean", -} diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/eucjpprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/eucjpprober.py deleted file mode 100644 index 39487f4..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/eucjpprober.py +++ /dev/null @@ -1,102 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Union - -from .chardistribution import EUCJPDistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .enums import MachineState, ProbingState -from .jpcntx import EUCJPContextAnalysis -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import EUCJP_SM_MODEL - - -class EUCJPProber(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(EUCJP_SM_MODEL) - self.distribution_analyzer = EUCJPDistributionAnalysis() - self.context_analyzer = EUCJPContextAnalysis() - self.reset() - - def reset(self) -> None: - super().reset() - self.context_analyzer.reset() - - @property - def charset_name(self) -> str: - return "EUC-JP" - - @property - def language(self) -> str: - return "Japanese" - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - assert self.coding_sm is not None - assert self.distribution_analyzer is not None - - for i, byte in enumerate(byte_str): - # PY3K: byte_str is a byte array, so byte is an int, not a byte - coding_state = self.coding_sm.next_state(byte) - if coding_state == MachineState.ERROR: - self.logger.debug( - "%s %s prober hit error at byte %s", - self.charset_name, - self.language, - i, - ) - self._state = ProbingState.NOT_ME - break - if coding_state == MachineState.ITS_ME: - self._state = ProbingState.FOUND_IT - break - if coding_state == MachineState.START: - char_len = self.coding_sm.get_current_charlen() - if i == 0: - self._last_char[1] = byte - self.context_analyzer.feed(self._last_char, char_len) - self.distribution_analyzer.feed(self._last_char, char_len) - else: - self.context_analyzer.feed(byte_str[i - 1 : i + 1], char_len) - self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len) - - self._last_char[0] = byte_str[-1] - - if self.state == ProbingState.DETECTING: - if self.context_analyzer.got_enough_data() and ( - self.get_confidence() > self.SHORTCUT_THRESHOLD - ): - self._state = ProbingState.FOUND_IT - - return self.state - - def get_confidence(self) -> float: - assert self.distribution_analyzer is not None - - context_conf = self.context_analyzer.get_confidence() - distrib_conf = self.distribution_analyzer.get_confidence() - return max(context_conf, distrib_conf) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euckrfreq.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euckrfreq.py deleted file mode 100644 index 7dc3b10..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euckrfreq.py +++ /dev/null @@ -1,196 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# Sampling from about 20M text materials include literature and computer technology - -# 128 --> 0.79 -# 256 --> 0.92 -# 512 --> 0.986 -# 1024 --> 0.99944 -# 2048 --> 0.99999 -# -# Idea Distribution Ratio = 0.98653 / (1-0.98653) = 73.24 -# Random Distribution Ration = 512 / (2350-512) = 0.279. -# -# Typical Distribution Ratio - -EUCKR_TYPICAL_DISTRIBUTION_RATIO = 6.0 - -EUCKR_TABLE_SIZE = 2352 - -# Char to FreqOrder table , -# fmt: off -EUCKR_CHAR_TO_FREQ_ORDER = ( - 13, 130, 120,1396, 481,1719,1720, 328, 609, 212,1721, 707, 400, 299,1722, 87, -1397,1723, 104, 536,1117,1203,1724,1267, 685,1268, 508,1725,1726,1727,1728,1398, -1399,1729,1730,1731, 141, 621, 326,1057, 368,1732, 267, 488, 20,1733,1269,1734, - 945,1400,1735, 47, 904,1270,1736,1737, 773, 248,1738, 409, 313, 786, 429,1739, - 116, 987, 813,1401, 683, 75,1204, 145,1740,1741,1742,1743, 16, 847, 667, 622, - 708,1744,1745,1746, 966, 787, 304, 129,1747, 60, 820, 123, 676,1748,1749,1750, -1751, 617,1752, 626,1753,1754,1755,1756, 653,1757,1758,1759,1760,1761,1762, 856, - 344,1763,1764,1765,1766, 89, 401, 418, 806, 905, 848,1767,1768,1769, 946,1205, - 709,1770,1118,1771, 241,1772,1773,1774,1271,1775, 569,1776, 999,1777,1778,1779, -1780, 337, 751,1058, 28, 628, 254,1781, 177, 906, 270, 349, 891,1079,1782, 19, -1783, 379,1784, 315,1785, 629, 754,1402, 559,1786, 636, 203,1206,1787, 710, 567, -1788, 935, 814,1789,1790,1207, 766, 528,1791,1792,1208,1793,1794,1795,1796,1797, -1403,1798,1799, 533,1059,1404,1405,1156,1406, 936, 884,1080,1800, 351,1801,1802, -1803,1804,1805, 801,1806,1807,1808,1119,1809,1157, 714, 474,1407,1810, 298, 899, - 885,1811,1120, 802,1158,1812, 892,1813,1814,1408, 659,1815,1816,1121,1817,1818, -1819,1820,1821,1822, 319,1823, 594, 545,1824, 815, 937,1209,1825,1826, 573,1409, -1022,1827,1210,1828,1829,1830,1831,1832,1833, 556, 722, 807,1122,1060,1834, 697, -1835, 900, 557, 715,1836,1410, 540,1411, 752,1159, 294, 597,1211, 976, 803, 770, -1412,1837,1838, 39, 794,1413, 358,1839, 371, 925,1840, 453, 661, 788, 531, 723, - 544,1023,1081, 869, 91,1841, 392, 430, 790, 602,1414, 677,1082, 457,1415,1416, -1842,1843, 475, 327,1024,1417, 795, 121,1844, 733, 403,1418,1845,1846,1847, 300, - 119, 711,1212, 627,1848,1272, 207,1849,1850, 796,1213, 382,1851, 519,1852,1083, - 893,1853,1854,1855, 367, 809, 487, 671,1856, 663,1857,1858, 956, 471, 306, 857, -1859,1860,1160,1084,1861,1862,1863,1864,1865,1061,1866,1867,1868,1869,1870,1871, - 282, 96, 574,1872, 502,1085,1873,1214,1874, 907,1875,1876, 827, 977,1419,1420, -1421, 268,1877,1422,1878,1879,1880, 308,1881, 2, 537,1882,1883,1215,1884,1885, - 127, 791,1886,1273,1423,1887, 34, 336, 404, 643,1888, 571, 654, 894, 840,1889, - 0, 886,1274, 122, 575, 260, 908, 938,1890,1275, 410, 316,1891,1892, 100,1893, -1894,1123, 48,1161,1124,1025,1895, 633, 901,1276,1896,1897, 115, 816,1898, 317, -1899, 694,1900, 909, 734,1424, 572, 866,1425, 691, 85, 524,1010, 543, 394, 841, -1901,1902,1903,1026,1904,1905,1906,1907,1908,1909, 30, 451, 651, 988, 310,1910, -1911,1426, 810,1216, 93,1912,1913,1277,1217,1914, 858, 759, 45, 58, 181, 610, - 269,1915,1916, 131,1062, 551, 443,1000, 821,1427, 957, 895,1086,1917,1918, 375, -1919, 359,1920, 687,1921, 822,1922, 293,1923,1924, 40, 662, 118, 692, 29, 939, - 887, 640, 482, 174,1925, 69,1162, 728,1428, 910,1926,1278,1218,1279, 386, 870, - 217, 854,1163, 823,1927,1928,1929,1930, 834,1931, 78,1932, 859,1933,1063,1934, -1935,1936,1937, 438,1164, 208, 595,1938,1939,1940,1941,1219,1125,1942, 280, 888, -1429,1430,1220,1431,1943,1944,1945,1946,1947,1280, 150, 510,1432,1948,1949,1950, -1951,1952,1953,1954,1011,1087,1955,1433,1043,1956, 881,1957, 614, 958,1064,1065, -1221,1958, 638,1001, 860, 967, 896,1434, 989, 492, 553,1281,1165,1959,1282,1002, -1283,1222,1960,1961,1962,1963, 36, 383, 228, 753, 247, 454,1964, 876, 678,1965, -1966,1284, 126, 464, 490, 835, 136, 672, 529, 940,1088,1435, 473,1967,1968, 467, - 50, 390, 227, 587, 279, 378, 598, 792, 968, 240, 151, 160, 849, 882,1126,1285, - 639,1044, 133, 140, 288, 360, 811, 563,1027, 561, 142, 523,1969,1970,1971, 7, - 103, 296, 439, 407, 506, 634, 990,1972,1973,1974,1975, 645,1976,1977,1978,1979, -1980,1981, 236,1982,1436,1983,1984,1089, 192, 828, 618, 518,1166, 333,1127,1985, - 818,1223,1986,1987,1988,1989,1990,1991,1992,1993, 342,1128,1286, 746, 842,1994, -1995, 560, 223,1287, 98, 8, 189, 650, 978,1288,1996,1437,1997, 17, 345, 250, - 423, 277, 234, 512, 226, 97, 289, 42, 167,1998, 201,1999,2000, 843, 836, 824, - 532, 338, 783,1090, 182, 576, 436,1438,1439, 527, 500,2001, 947, 889,2002,2003, -2004,2005, 262, 600, 314, 447,2006, 547,2007, 693, 738,1129,2008, 71,1440, 745, - 619, 688,2009, 829,2010,2011, 147,2012, 33, 948,2013,2014, 74, 224,2015, 61, - 191, 918, 399, 637,2016,1028,1130, 257, 902,2017,2018,2019,2020,2021,2022,2023, -2024,2025,2026, 837,2027,2028,2029,2030, 179, 874, 591, 52, 724, 246,2031,2032, -2033,2034,1167, 969,2035,1289, 630, 605, 911,1091,1168,2036,2037,2038,1441, 912, -2039, 623,2040,2041, 253,1169,1290,2042,1442, 146, 620, 611, 577, 433,2043,1224, - 719,1170, 959, 440, 437, 534, 84, 388, 480,1131, 159, 220, 198, 679,2044,1012, - 819,1066,1443, 113,1225, 194, 318,1003,1029,2045,2046,2047,2048,1067,2049,2050, -2051,2052,2053, 59, 913, 112,2054, 632,2055, 455, 144, 739,1291,2056, 273, 681, - 499,2057, 448,2058,2059, 760,2060,2061, 970, 384, 169, 245,1132,2062,2063, 414, -1444,2064,2065, 41, 235,2066, 157, 252, 877, 568, 919, 789, 580,2067, 725,2068, -2069,1292,2070,2071,1445,2072,1446,2073,2074, 55, 588, 66,1447, 271,1092,2075, -1226,2076, 960,1013, 372,2077,2078,2079,2080,2081,1293,2082,2083,2084,2085, 850, -2086,2087,2088,2089,2090, 186,2091,1068, 180,2092,2093,2094, 109,1227, 522, 606, -2095, 867,1448,1093, 991,1171, 926, 353,1133,2096, 581,2097,2098,2099,1294,1449, -1450,2100, 596,1172,1014,1228,2101,1451,1295,1173,1229,2102,2103,1296,1134,1452, - 949,1135,2104,2105,1094,1453,1454,1455,2106,1095,2107,2108,2109,2110,2111,2112, -2113,2114,2115,2116,2117, 804,2118,2119,1230,1231, 805,1456, 405,1136,2120,2121, -2122,2123,2124, 720, 701,1297, 992,1457, 927,1004,2125,2126,2127,2128,2129,2130, - 22, 417,2131, 303,2132, 385,2133, 971, 520, 513,2134,1174, 73,1096, 231, 274, - 962,1458, 673,2135,1459,2136, 152,1137,2137,2138,2139,2140,1005,1138,1460,1139, -2141,2142,2143,2144, 11, 374, 844,2145, 154,1232, 46,1461,2146, 838, 830, 721, -1233, 106,2147, 90, 428, 462, 578, 566,1175, 352,2148,2149, 538,1234, 124,1298, -2150,1462, 761, 565,2151, 686,2152, 649,2153, 72, 173,2154, 460, 415,2155,1463, -2156,1235, 305,2157,2158,2159,2160,2161,2162, 579,2163,2164,2165,2166,2167, 747, -2168,2169,2170,2171,1464, 669,2172,2173,2174,2175,2176,1465,2177, 23, 530, 285, -2178, 335, 729,2179, 397,2180,2181,2182,1030,2183,2184, 698,2185,2186, 325,2187, -2188, 369,2189, 799,1097,1015, 348,2190,1069, 680,2191, 851,1466,2192,2193, 10, -2194, 613, 424,2195, 979, 108, 449, 589, 27, 172, 81,1031, 80, 774, 281, 350, -1032, 525, 301, 582,1176,2196, 674,1045,2197,2198,1467, 730, 762,2199,2200,2201, -2202,1468,2203, 993,2204,2205, 266,1070, 963,1140,2206,2207,2208, 664,1098, 972, -2209,2210,2211,1177,1469,1470, 871,2212,2213,2214,2215,2216,1471,2217,2218,2219, -2220,2221,2222,2223,2224,2225,2226,2227,1472,1236,2228,2229,2230,2231,2232,2233, -2234,2235,1299,2236,2237, 200,2238, 477, 373,2239,2240, 731, 825, 777,2241,2242, -2243, 521, 486, 548,2244,2245,2246,1473,1300, 53, 549, 137, 875, 76, 158,2247, -1301,1474, 469, 396,1016, 278, 712,2248, 321, 442, 503, 767, 744, 941,1237,1178, -1475,2249, 82, 178,1141,1179, 973,2250,1302,2251, 297,2252,2253, 570,2254,2255, -2256, 18, 450, 206,2257, 290, 292,1142,2258, 511, 162, 99, 346, 164, 735,2259, -1476,1477, 4, 554, 343, 798,1099,2260,1100,2261, 43, 171,1303, 139, 215,2262, -2263, 717, 775,2264,1033, 322, 216,2265, 831,2266, 149,2267,1304,2268,2269, 702, -1238, 135, 845, 347, 309,2270, 484,2271, 878, 655, 238,1006,1478,2272, 67,2273, - 295,2274,2275, 461,2276, 478, 942, 412,2277,1034,2278,2279,2280, 265,2281, 541, -2282,2283,2284,2285,2286, 70, 852,1071,2287,2288,2289,2290, 21, 56, 509, 117, - 432,2291,2292, 331, 980, 552,1101, 148, 284, 105, 393,1180,1239, 755,2293, 187, -2294,1046,1479,2295, 340,2296, 63,1047, 230,2297,2298,1305, 763,1306, 101, 800, - 808, 494,2299,2300,2301, 903,2302, 37,1072, 14, 5,2303, 79, 675,2304, 312, -2305,2306,2307,2308,2309,1480, 6,1307,2310,2311,2312, 1, 470, 35, 24, 229, -2313, 695, 210, 86, 778, 15, 784, 592, 779, 32, 77, 855, 964,2314, 259,2315, - 501, 380,2316,2317, 83, 981, 153, 689,1308,1481,1482,1483,2318,2319, 716,1484, -2320,2321,2322,2323,2324,2325,1485,2326,2327, 128, 57, 68, 261,1048, 211, 170, -1240, 31,2328, 51, 435, 742,2329,2330,2331, 635,2332, 264, 456,2333,2334,2335, - 425,2336,1486, 143, 507, 263, 943,2337, 363, 920,1487, 256,1488,1102, 243, 601, -1489,2338,2339,2340,2341,2342,2343,2344, 861,2345,2346,2347,2348,2349,2350, 395, -2351,1490,1491, 62, 535, 166, 225,2352,2353, 668, 419,1241, 138, 604, 928,2354, -1181,2355,1492,1493,2356,2357,2358,1143,2359, 696,2360, 387, 307,1309, 682, 476, -2361,2362, 332, 12, 222, 156,2363, 232,2364, 641, 276, 656, 517,1494,1495,1035, - 416, 736,1496,2365,1017, 586,2366,2367,2368,1497,2369, 242,2370,2371,2372,1498, -2373, 965, 713,2374,2375,2376,2377, 740, 982,1499, 944,1500,1007,2378,2379,1310, -1501,2380,2381,2382, 785, 329,2383,2384,1502,2385,2386,2387, 932,2388,1503,2389, -2390,2391,2392,1242,2393,2394,2395,2396,2397, 994, 950,2398,2399,2400,2401,1504, -1311,2402,2403,2404,2405,1049, 749,2406,2407, 853, 718,1144,1312,2408,1182,1505, -2409,2410, 255, 516, 479, 564, 550, 214,1506,1507,1313, 413, 239, 444, 339,1145, -1036,1508,1509,1314,1037,1510,1315,2411,1511,2412,2413,2414, 176, 703, 497, 624, - 593, 921, 302,2415, 341, 165,1103,1512,2416,1513,2417,2418,2419, 376,2420, 700, -2421,2422,2423, 258, 768,1316,2424,1183,2425, 995, 608,2426,2427,2428,2429, 221, -2430,2431,2432,2433,2434,2435,2436,2437, 195, 323, 726, 188, 897, 983,1317, 377, - 644,1050, 879,2438, 452,2439,2440,2441,2442,2443,2444, 914,2445,2446,2447,2448, - 915, 489,2449,1514,1184,2450,2451, 515, 64, 427, 495,2452, 583,2453, 483, 485, -1038, 562, 213,1515, 748, 666,2454,2455,2456,2457, 334,2458, 780, 996,1008, 705, -1243,2459,2460,2461,2462,2463, 114,2464, 493,1146, 366, 163,1516, 961,1104,2465, - 291,2466,1318,1105,2467,1517, 365,2468, 355, 951,1244,2469,1319,2470, 631,2471, -2472, 218,1320, 364, 320, 756,1518,1519,1321,1520,1322,2473,2474,2475,2476, 997, -2477,2478,2479,2480, 665,1185,2481, 916,1521,2482,2483,2484, 584, 684,2485,2486, - 797,2487,1051,1186,2488,2489,2490,1522,2491,2492, 370,2493,1039,1187, 65,2494, - 434, 205, 463,1188,2495, 125, 812, 391, 402, 826, 699, 286, 398, 155, 781, 771, - 585,2496, 590, 505,1073,2497, 599, 244, 219, 917,1018, 952, 646,1523,2498,1323, -2499,2500, 49, 984, 354, 741,2501, 625,2502,1324,2503,1019, 190, 357, 757, 491, - 95, 782, 868,2504,2505,2506,2507,2508,2509, 134,1524,1074, 422,1525, 898,2510, - 161,2511,2512,2513,2514, 769,2515,1526,2516,2517, 411,1325,2518, 472,1527,2519, -2520,2521,2522,2523,2524, 985,2525,2526,2527,2528,2529,2530, 764,2531,1245,2532, -2533, 25, 204, 311,2534, 496,2535,1052,2536,2537,2538,2539,2540,2541,2542, 199, - 704, 504, 468, 758, 657,1528, 196, 44, 839,1246, 272, 750,2543, 765, 862,2544, -2545,1326,2546, 132, 615, 933,2547, 732,2548,2549,2550,1189,1529,2551, 283,1247, -1053, 607, 929,2552,2553,2554, 930, 183, 872, 616,1040,1147,2555,1148,1020, 441, - 249,1075,2556,2557,2558, 466, 743,2559,2560,2561, 92, 514, 426, 420, 526,2562, -2563,2564,2565,2566,2567,2568, 185,2569,2570,2571,2572, 776,1530, 658,2573, 362, -2574, 361, 922,1076, 793,2575,2576,2577,2578,2579,2580,1531, 251,2581,2582,2583, -2584,1532, 54, 612, 237,1327,2585,2586, 275, 408, 647, 111,2587,1533,1106, 465, - 3, 458, 9, 38,2588, 107, 110, 890, 209, 26, 737, 498,2589,1534,2590, 431, - 202, 88,1535, 356, 287,1107, 660,1149,2591, 381,1536, 986,1150, 445,1248,1151, - 974,2592,2593, 846,2594, 446, 953, 184,1249,1250, 727,2595, 923, 193, 883,2596, -2597,2598, 102, 324, 539, 817,2599, 421,1041,2600, 832,2601, 94, 175, 197, 406, -2602, 459,2603,2604,2605,2606,2607, 330, 555,2608,2609,2610, 706,1108, 389,2611, -2612,2613,2614, 233,2615, 833, 558, 931, 954,1251,2616,2617,1537, 546,2618,2619, -1009,2620,2621,2622,1538, 690,1328,2623, 955,2624,1539,2625,2626, 772,2627,2628, -2629,2630,2631, 924, 648, 863, 603,2632,2633, 934,1540, 864, 865,2634, 642,1042, - 670,1190,2635,2636,2637,2638, 168,2639, 652, 873, 542,1054,1541,2640,2641,2642, # 512, 256 -) -# fmt: on diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euckrprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euckrprober.py deleted file mode 100644 index 1fc5de0..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euckrprober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .chardistribution import EUCKRDistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import EUCKR_SM_MODEL - - -class EUCKRProber(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(EUCKR_SM_MODEL) - self.distribution_analyzer = EUCKRDistributionAnalysis() - self.reset() - - @property - def charset_name(self) -> str: - return "EUC-KR" - - @property - def language(self) -> str: - return "Korean" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euctwfreq.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euctwfreq.py deleted file mode 100644 index 4900ccc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euctwfreq.py +++ /dev/null @@ -1,388 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# EUCTW frequency table -# Converted from big5 work -# by Taiwan's Mandarin Promotion Council -# - -# 128 --> 0.42261 -# 256 --> 0.57851 -# 512 --> 0.74851 -# 1024 --> 0.89384 -# 2048 --> 0.97583 -# -# Idea Distribution Ratio = 0.74851/(1-0.74851) =2.98 -# Random Distribution Ration = 512/(5401-512)=0.105 -# -# Typical Distribution Ratio about 25% of Ideal one, still much higher than RDR - -EUCTW_TYPICAL_DISTRIBUTION_RATIO = 0.75 - -# Char to FreqOrder table -EUCTW_TABLE_SIZE = 5376 - -# fmt: off -EUCTW_CHAR_TO_FREQ_ORDER = ( - 1, 1800, 1506, 255, 1431, 198, 9, 82, 6, 7310, 177, 202, 3615, 1256, 2808, 110, # 2742 - 3735, 33, 3241, 261, 76, 44, 2113, 16, 2931, 2184, 1176, 659, 3868, 26, 3404, 2643, # 2758 - 1198, 3869, 3313, 4060, 410, 2211, 302, 590, 361, 1963, 8, 204, 58, 4296, 7311, 1931, # 2774 - 63, 7312, 7313, 317, 1614, 75, 222, 159, 4061, 2412, 1480, 7314, 3500, 3068, 224, 2809, # 2790 - 3616, 3, 10, 3870, 1471, 29, 2774, 1135, 2852, 1939, 873, 130, 3242, 1123, 312, 7315, # 2806 - 4297, 2051, 507, 252, 682, 7316, 142, 1914, 124, 206, 2932, 34, 3501, 3173, 64, 604, # 2822 - 7317, 2494, 1976, 1977, 155, 1990, 645, 641, 1606, 7318, 3405, 337, 72, 406, 7319, 80, # 2838 - 630, 238, 3174, 1509, 263, 939, 1092, 2644, 756, 1440, 1094, 3406, 449, 69, 2969, 591, # 2854 - 179, 2095, 471, 115, 2034, 1843, 60, 50, 2970, 134, 806, 1868, 734, 2035, 3407, 180, # 2870 - 995, 1607, 156, 537, 2893, 688, 7320, 319, 1305, 779, 2144, 514, 2374, 298, 4298, 359, # 2886 - 2495, 90, 2707, 1338, 663, 11, 906, 1099, 2545, 20, 2436, 182, 532, 1716, 7321, 732, # 2902 - 1376, 4062, 1311, 1420, 3175, 25, 2312, 1056, 113, 399, 382, 1949, 242, 3408, 2467, 529, # 2918 - 3243, 475, 1447, 3617, 7322, 117, 21, 656, 810, 1297, 2295, 2329, 3502, 7323, 126, 4063, # 2934 - 706, 456, 150, 613, 4299, 71, 1118, 2036, 4064, 145, 3069, 85, 835, 486, 2114, 1246, # 2950 - 1426, 428, 727, 1285, 1015, 800, 106, 623, 303, 1281, 7324, 2127, 2354, 347, 3736, 221, # 2966 - 3503, 3110, 7325, 1955, 1153, 4065, 83, 296, 1199, 3070, 192, 624, 93, 7326, 822, 1897, # 2982 - 2810, 3111, 795, 2064, 991, 1554, 1542, 1592, 27, 43, 2853, 859, 139, 1456, 860, 4300, # 2998 - 437, 712, 3871, 164, 2392, 3112, 695, 211, 3017, 2096, 195, 3872, 1608, 3504, 3505, 3618, # 3014 - 3873, 234, 811, 2971, 2097, 3874, 2229, 1441, 3506, 1615, 2375, 668, 2076, 1638, 305, 228, # 3030 - 1664, 4301, 467, 415, 7327, 262, 2098, 1593, 239, 108, 300, 200, 1033, 512, 1247, 2077, # 3046 - 7328, 7329, 2173, 3176, 3619, 2673, 593, 845, 1062, 3244, 88, 1723, 2037, 3875, 1950, 212, # 3062 - 266, 152, 149, 468, 1898, 4066, 4302, 77, 187, 7330, 3018, 37, 5, 2972, 7331, 3876, # 3078 - 7332, 7333, 39, 2517, 4303, 2894, 3177, 2078, 55, 148, 74, 4304, 545, 483, 1474, 1029, # 3094 - 1665, 217, 1869, 1531, 3113, 1104, 2645, 4067, 24, 172, 3507, 900, 3877, 3508, 3509, 4305, # 3110 - 32, 1408, 2811, 1312, 329, 487, 2355, 2247, 2708, 784, 2674, 4, 3019, 3314, 1427, 1788, # 3126 - 188, 109, 499, 7334, 3620, 1717, 1789, 888, 1217, 3020, 4306, 7335, 3510, 7336, 3315, 1520, # 3142 - 3621, 3878, 196, 1034, 775, 7337, 7338, 929, 1815, 249, 439, 38, 7339, 1063, 7340, 794, # 3158 - 3879, 1435, 2296, 46, 178, 3245, 2065, 7341, 2376, 7342, 214, 1709, 4307, 804, 35, 707, # 3174 - 324, 3622, 1601, 2546, 140, 459, 4068, 7343, 7344, 1365, 839, 272, 978, 2257, 2572, 3409, # 3190 - 2128, 1363, 3623, 1423, 697, 100, 3071, 48, 70, 1231, 495, 3114, 2193, 7345, 1294, 7346, # 3206 - 2079, 462, 586, 1042, 3246, 853, 256, 988, 185, 2377, 3410, 1698, 434, 1084, 7347, 3411, # 3222 - 314, 2615, 2775, 4308, 2330, 2331, 569, 2280, 637, 1816, 2518, 757, 1162, 1878, 1616, 3412, # 3238 - 287, 1577, 2115, 768, 4309, 1671, 2854, 3511, 2519, 1321, 3737, 909, 2413, 7348, 4069, 933, # 3254 - 3738, 7349, 2052, 2356, 1222, 4310, 765, 2414, 1322, 786, 4311, 7350, 1919, 1462, 1677, 2895, # 3270 - 1699, 7351, 4312, 1424, 2437, 3115, 3624, 2590, 3316, 1774, 1940, 3413, 3880, 4070, 309, 1369, # 3286 - 1130, 2812, 364, 2230, 1653, 1299, 3881, 3512, 3882, 3883, 2646, 525, 1085, 3021, 902, 2000, # 3302 - 1475, 964, 4313, 421, 1844, 1415, 1057, 2281, 940, 1364, 3116, 376, 4314, 4315, 1381, 7, # 3318 - 2520, 983, 2378, 336, 1710, 2675, 1845, 321, 3414, 559, 1131, 3022, 2742, 1808, 1132, 1313, # 3334 - 265, 1481, 1857, 7352, 352, 1203, 2813, 3247, 167, 1089, 420, 2814, 776, 792, 1724, 3513, # 3350 - 4071, 2438, 3248, 7353, 4072, 7354, 446, 229, 333, 2743, 901, 3739, 1200, 1557, 4316, 2647, # 3366 - 1920, 395, 2744, 2676, 3740, 4073, 1835, 125, 916, 3178, 2616, 4317, 7355, 7356, 3741, 7357, # 3382 - 7358, 7359, 4318, 3117, 3625, 1133, 2547, 1757, 3415, 1510, 2313, 1409, 3514, 7360, 2145, 438, # 3398 - 2591, 2896, 2379, 3317, 1068, 958, 3023, 461, 311, 2855, 2677, 4074, 1915, 3179, 4075, 1978, # 3414 - 383, 750, 2745, 2617, 4076, 274, 539, 385, 1278, 1442, 7361, 1154, 1964, 384, 561, 210, # 3430 - 98, 1295, 2548, 3515, 7362, 1711, 2415, 1482, 3416, 3884, 2897, 1257, 129, 7363, 3742, 642, # 3446 - 523, 2776, 2777, 2648, 7364, 141, 2231, 1333, 68, 176, 441, 876, 907, 4077, 603, 2592, # 3462 - 710, 171, 3417, 404, 549, 18, 3118, 2393, 1410, 3626, 1666, 7365, 3516, 4319, 2898, 4320, # 3478 - 7366, 2973, 368, 7367, 146, 366, 99, 871, 3627, 1543, 748, 807, 1586, 1185, 22, 2258, # 3494 - 379, 3743, 3180, 7368, 3181, 505, 1941, 2618, 1991, 1382, 2314, 7369, 380, 2357, 218, 702, # 3510 - 1817, 1248, 3418, 3024, 3517, 3318, 3249, 7370, 2974, 3628, 930, 3250, 3744, 7371, 59, 7372, # 3526 - 585, 601, 4078, 497, 3419, 1112, 1314, 4321, 1801, 7373, 1223, 1472, 2174, 7374, 749, 1836, # 3542 - 690, 1899, 3745, 1772, 3885, 1476, 429, 1043, 1790, 2232, 2116, 917, 4079, 447, 1086, 1629, # 3558 - 7375, 556, 7376, 7377, 2020, 1654, 844, 1090, 105, 550, 966, 1758, 2815, 1008, 1782, 686, # 3574 - 1095, 7378, 2282, 793, 1602, 7379, 3518, 2593, 4322, 4080, 2933, 2297, 4323, 3746, 980, 2496, # 3590 - 544, 353, 527, 4324, 908, 2678, 2899, 7380, 381, 2619, 1942, 1348, 7381, 1341, 1252, 560, # 3606 - 3072, 7382, 3420, 2856, 7383, 2053, 973, 886, 2080, 143, 4325, 7384, 7385, 157, 3886, 496, # 3622 - 4081, 57, 840, 540, 2038, 4326, 4327, 3421, 2117, 1445, 970, 2259, 1748, 1965, 2081, 4082, # 3638 - 3119, 1234, 1775, 3251, 2816, 3629, 773, 1206, 2129, 1066, 2039, 1326, 3887, 1738, 1725, 4083, # 3654 - 279, 3120, 51, 1544, 2594, 423, 1578, 2130, 2066, 173, 4328, 1879, 7386, 7387, 1583, 264, # 3670 - 610, 3630, 4329, 2439, 280, 154, 7388, 7389, 7390, 1739, 338, 1282, 3073, 693, 2857, 1411, # 3686 - 1074, 3747, 2440, 7391, 4330, 7392, 7393, 1240, 952, 2394, 7394, 2900, 1538, 2679, 685, 1483, # 3702 - 4084, 2468, 1436, 953, 4085, 2054, 4331, 671, 2395, 79, 4086, 2441, 3252, 608, 567, 2680, # 3718 - 3422, 4087, 4088, 1691, 393, 1261, 1791, 2396, 7395, 4332, 7396, 7397, 7398, 7399, 1383, 1672, # 3734 - 3748, 3182, 1464, 522, 1119, 661, 1150, 216, 675, 4333, 3888, 1432, 3519, 609, 4334, 2681, # 3750 - 2397, 7400, 7401, 7402, 4089, 3025, 0, 7403, 2469, 315, 231, 2442, 301, 3319, 4335, 2380, # 3766 - 7404, 233, 4090, 3631, 1818, 4336, 4337, 7405, 96, 1776, 1315, 2082, 7406, 257, 7407, 1809, # 3782 - 3632, 2709, 1139, 1819, 4091, 2021, 1124, 2163, 2778, 1777, 2649, 7408, 3074, 363, 1655, 3183, # 3798 - 7409, 2975, 7410, 7411, 7412, 3889, 1567, 3890, 718, 103, 3184, 849, 1443, 341, 3320, 2934, # 3814 - 1484, 7413, 1712, 127, 67, 339, 4092, 2398, 679, 1412, 821, 7414, 7415, 834, 738, 351, # 3830 - 2976, 2146, 846, 235, 1497, 1880, 418, 1992, 3749, 2710, 186, 1100, 2147, 2746, 3520, 1545, # 3846 - 1355, 2935, 2858, 1377, 583, 3891, 4093, 2573, 2977, 7416, 1298, 3633, 1078, 2549, 3634, 2358, # 3862 - 78, 3750, 3751, 267, 1289, 2099, 2001, 1594, 4094, 348, 369, 1274, 2194, 2175, 1837, 4338, # 3878 - 1820, 2817, 3635, 2747, 2283, 2002, 4339, 2936, 2748, 144, 3321, 882, 4340, 3892, 2749, 3423, # 3894 - 4341, 2901, 7417, 4095, 1726, 320, 7418, 3893, 3026, 788, 2978, 7419, 2818, 1773, 1327, 2859, # 3910 - 3894, 2819, 7420, 1306, 4342, 2003, 1700, 3752, 3521, 2359, 2650, 787, 2022, 506, 824, 3636, # 3926 - 534, 323, 4343, 1044, 3322, 2023, 1900, 946, 3424, 7421, 1778, 1500, 1678, 7422, 1881, 4344, # 3942 - 165, 243, 4345, 3637, 2521, 123, 683, 4096, 764, 4346, 36, 3895, 1792, 589, 2902, 816, # 3958 - 626, 1667, 3027, 2233, 1639, 1555, 1622, 3753, 3896, 7423, 3897, 2860, 1370, 1228, 1932, 891, # 3974 - 2083, 2903, 304, 4097, 7424, 292, 2979, 2711, 3522, 691, 2100, 4098, 1115, 4347, 118, 662, # 3990 - 7425, 611, 1156, 854, 2381, 1316, 2861, 2, 386, 515, 2904, 7426, 7427, 3253, 868, 2234, # 4006 - 1486, 855, 2651, 785, 2212, 3028, 7428, 1040, 3185, 3523, 7429, 3121, 448, 7430, 1525, 7431, # 4022 - 2164, 4348, 7432, 3754, 7433, 4099, 2820, 3524, 3122, 503, 818, 3898, 3123, 1568, 814, 676, # 4038 - 1444, 306, 1749, 7434, 3755, 1416, 1030, 197, 1428, 805, 2821, 1501, 4349, 7435, 7436, 7437, # 4054 - 1993, 7438, 4350, 7439, 7440, 2195, 13, 2779, 3638, 2980, 3124, 1229, 1916, 7441, 3756, 2131, # 4070 - 7442, 4100, 4351, 2399, 3525, 7443, 2213, 1511, 1727, 1120, 7444, 7445, 646, 3757, 2443, 307, # 4086 - 7446, 7447, 1595, 3186, 7448, 7449, 7450, 3639, 1113, 1356, 3899, 1465, 2522, 2523, 7451, 519, # 4102 - 7452, 128, 2132, 92, 2284, 1979, 7453, 3900, 1512, 342, 3125, 2196, 7454, 2780, 2214, 1980, # 4118 - 3323, 7455, 290, 1656, 1317, 789, 827, 2360, 7456, 3758, 4352, 562, 581, 3901, 7457, 401, # 4134 - 4353, 2248, 94, 4354, 1399, 2781, 7458, 1463, 2024, 4355, 3187, 1943, 7459, 828, 1105, 4101, # 4150 - 1262, 1394, 7460, 4102, 605, 4356, 7461, 1783, 2862, 7462, 2822, 819, 2101, 578, 2197, 2937, # 4166 - 7463, 1502, 436, 3254, 4103, 3255, 2823, 3902, 2905, 3425, 3426, 7464, 2712, 2315, 7465, 7466, # 4182 - 2332, 2067, 23, 4357, 193, 826, 3759, 2102, 699, 1630, 4104, 3075, 390, 1793, 1064, 3526, # 4198 - 7467, 1579, 3076, 3077, 1400, 7468, 4105, 1838, 1640, 2863, 7469, 4358, 4359, 137, 4106, 598, # 4214 - 3078, 1966, 780, 104, 974, 2938, 7470, 278, 899, 253, 402, 572, 504, 493, 1339, 7471, # 4230 - 3903, 1275, 4360, 2574, 2550, 7472, 3640, 3029, 3079, 2249, 565, 1334, 2713, 863, 41, 7473, # 4246 - 7474, 4361, 7475, 1657, 2333, 19, 463, 2750, 4107, 606, 7476, 2981, 3256, 1087, 2084, 1323, # 4262 - 2652, 2982, 7477, 1631, 1623, 1750, 4108, 2682, 7478, 2864, 791, 2714, 2653, 2334, 232, 2416, # 4278 - 7479, 2983, 1498, 7480, 2654, 2620, 755, 1366, 3641, 3257, 3126, 2025, 1609, 119, 1917, 3427, # 4294 - 862, 1026, 4109, 7481, 3904, 3760, 4362, 3905, 4363, 2260, 1951, 2470, 7482, 1125, 817, 4110, # 4310 - 4111, 3906, 1513, 1766, 2040, 1487, 4112, 3030, 3258, 2824, 3761, 3127, 7483, 7484, 1507, 7485, # 4326 - 2683, 733, 40, 1632, 1106, 2865, 345, 4113, 841, 2524, 230, 4364, 2984, 1846, 3259, 3428, # 4342 - 7486, 1263, 986, 3429, 7487, 735, 879, 254, 1137, 857, 622, 1300, 1180, 1388, 1562, 3907, # 4358 - 3908, 2939, 967, 2751, 2655, 1349, 592, 2133, 1692, 3324, 2985, 1994, 4114, 1679, 3909, 1901, # 4374 - 2185, 7488, 739, 3642, 2715, 1296, 1290, 7489, 4115, 2198, 2199, 1921, 1563, 2595, 2551, 1870, # 4390 - 2752, 2986, 7490, 435, 7491, 343, 1108, 596, 17, 1751, 4365, 2235, 3430, 3643, 7492, 4366, # 4406 - 294, 3527, 2940, 1693, 477, 979, 281, 2041, 3528, 643, 2042, 3644, 2621, 2782, 2261, 1031, # 4422 - 2335, 2134, 2298, 3529, 4367, 367, 1249, 2552, 7493, 3530, 7494, 4368, 1283, 3325, 2004, 240, # 4438 - 1762, 3326, 4369, 4370, 836, 1069, 3128, 474, 7495, 2148, 2525, 268, 3531, 7496, 3188, 1521, # 4454 - 1284, 7497, 1658, 1546, 4116, 7498, 3532, 3533, 7499, 4117, 3327, 2684, 1685, 4118, 961, 1673, # 4470 - 2622, 190, 2005, 2200, 3762, 4371, 4372, 7500, 570, 2497, 3645, 1490, 7501, 4373, 2623, 3260, # 4486 - 1956, 4374, 584, 1514, 396, 1045, 1944, 7502, 4375, 1967, 2444, 7503, 7504, 4376, 3910, 619, # 4502 - 7505, 3129, 3261, 215, 2006, 2783, 2553, 3189, 4377, 3190, 4378, 763, 4119, 3763, 4379, 7506, # 4518 - 7507, 1957, 1767, 2941, 3328, 3646, 1174, 452, 1477, 4380, 3329, 3130, 7508, 2825, 1253, 2382, # 4534 - 2186, 1091, 2285, 4120, 492, 7509, 638, 1169, 1824, 2135, 1752, 3911, 648, 926, 1021, 1324, # 4550 - 4381, 520, 4382, 997, 847, 1007, 892, 4383, 3764, 2262, 1871, 3647, 7510, 2400, 1784, 4384, # 4566 - 1952, 2942, 3080, 3191, 1728, 4121, 2043, 3648, 4385, 2007, 1701, 3131, 1551, 30, 2263, 4122, # 4582 - 7511, 2026, 4386, 3534, 7512, 501, 7513, 4123, 594, 3431, 2165, 1821, 3535, 3432, 3536, 3192, # 4598 - 829, 2826, 4124, 7514, 1680, 3132, 1225, 4125, 7515, 3262, 4387, 4126, 3133, 2336, 7516, 4388, # 4614 - 4127, 7517, 3912, 3913, 7518, 1847, 2383, 2596, 3330, 7519, 4389, 374, 3914, 652, 4128, 4129, # 4630 - 375, 1140, 798, 7520, 7521, 7522, 2361, 4390, 2264, 546, 1659, 138, 3031, 2445, 4391, 7523, # 4646 - 2250, 612, 1848, 910, 796, 3765, 1740, 1371, 825, 3766, 3767, 7524, 2906, 2554, 7525, 692, # 4662 - 444, 3032, 2624, 801, 4392, 4130, 7526, 1491, 244, 1053, 3033, 4131, 4132, 340, 7527, 3915, # 4678 - 1041, 2987, 293, 1168, 87, 1357, 7528, 1539, 959, 7529, 2236, 721, 694, 4133, 3768, 219, # 4694 - 1478, 644, 1417, 3331, 2656, 1413, 1401, 1335, 1389, 3916, 7530, 7531, 2988, 2362, 3134, 1825, # 4710 - 730, 1515, 184, 2827, 66, 4393, 7532, 1660, 2943, 246, 3332, 378, 1457, 226, 3433, 975, # 4726 - 3917, 2944, 1264, 3537, 674, 696, 7533, 163, 7534, 1141, 2417, 2166, 713, 3538, 3333, 4394, # 4742 - 3918, 7535, 7536, 1186, 15, 7537, 1079, 1070, 7538, 1522, 3193, 3539, 276, 1050, 2716, 758, # 4758 - 1126, 653, 2945, 3263, 7539, 2337, 889, 3540, 3919, 3081, 2989, 903, 1250, 4395, 3920, 3434, # 4774 - 3541, 1342, 1681, 1718, 766, 3264, 286, 89, 2946, 3649, 7540, 1713, 7541, 2597, 3334, 2990, # 4790 - 7542, 2947, 2215, 3194, 2866, 7543, 4396, 2498, 2526, 181, 387, 1075, 3921, 731, 2187, 3335, # 4806 - 7544, 3265, 310, 313, 3435, 2299, 770, 4134, 54, 3034, 189, 4397, 3082, 3769, 3922, 7545, # 4822 - 1230, 1617, 1849, 355, 3542, 4135, 4398, 3336, 111, 4136, 3650, 1350, 3135, 3436, 3035, 4137, # 4838 - 2149, 3266, 3543, 7546, 2784, 3923, 3924, 2991, 722, 2008, 7547, 1071, 247, 1207, 2338, 2471, # 4854 - 1378, 4399, 2009, 864, 1437, 1214, 4400, 373, 3770, 1142, 2216, 667, 4401, 442, 2753, 2555, # 4870 - 3771, 3925, 1968, 4138, 3267, 1839, 837, 170, 1107, 934, 1336, 1882, 7548, 7549, 2118, 4139, # 4886 - 2828, 743, 1569, 7550, 4402, 4140, 582, 2384, 1418, 3437, 7551, 1802, 7552, 357, 1395, 1729, # 4902 - 3651, 3268, 2418, 1564, 2237, 7553, 3083, 3772, 1633, 4403, 1114, 2085, 4141, 1532, 7554, 482, # 4918 - 2446, 4404, 7555, 7556, 1492, 833, 1466, 7557, 2717, 3544, 1641, 2829, 7558, 1526, 1272, 3652, # 4934 - 4142, 1686, 1794, 416, 2556, 1902, 1953, 1803, 7559, 3773, 2785, 3774, 1159, 2316, 7560, 2867, # 4950 - 4405, 1610, 1584, 3036, 2419, 2754, 443, 3269, 1163, 3136, 7561, 7562, 3926, 7563, 4143, 2499, # 4966 - 3037, 4406, 3927, 3137, 2103, 1647, 3545, 2010, 1872, 4144, 7564, 4145, 431, 3438, 7565, 250, # 4982 - 97, 81, 4146, 7566, 1648, 1850, 1558, 160, 848, 7567, 866, 740, 1694, 7568, 2201, 2830, # 4998 - 3195, 4147, 4407, 3653, 1687, 950, 2472, 426, 469, 3196, 3654, 3655, 3928, 7569, 7570, 1188, # 5014 - 424, 1995, 861, 3546, 4148, 3775, 2202, 2685, 168, 1235, 3547, 4149, 7571, 2086, 1674, 4408, # 5030 - 3337, 3270, 220, 2557, 1009, 7572, 3776, 670, 2992, 332, 1208, 717, 7573, 7574, 3548, 2447, # 5046 - 3929, 3338, 7575, 513, 7576, 1209, 2868, 3339, 3138, 4409, 1080, 7577, 7578, 7579, 7580, 2527, # 5062 - 3656, 3549, 815, 1587, 3930, 3931, 7581, 3550, 3439, 3777, 1254, 4410, 1328, 3038, 1390, 3932, # 5078 - 1741, 3933, 3778, 3934, 7582, 236, 3779, 2448, 3271, 7583, 7584, 3657, 3780, 1273, 3781, 4411, # 5094 - 7585, 308, 7586, 4412, 245, 4413, 1851, 2473, 1307, 2575, 430, 715, 2136, 2449, 7587, 270, # 5110 - 199, 2869, 3935, 7588, 3551, 2718, 1753, 761, 1754, 725, 1661, 1840, 4414, 3440, 3658, 7589, # 5126 - 7590, 587, 14, 3272, 227, 2598, 326, 480, 2265, 943, 2755, 3552, 291, 650, 1883, 7591, # 5142 - 1702, 1226, 102, 1547, 62, 3441, 904, 4415, 3442, 1164, 4150, 7592, 7593, 1224, 1548, 2756, # 5158 - 391, 498, 1493, 7594, 1386, 1419, 7595, 2055, 1177, 4416, 813, 880, 1081, 2363, 566, 1145, # 5174 - 4417, 2286, 1001, 1035, 2558, 2599, 2238, 394, 1286, 7596, 7597, 2068, 7598, 86, 1494, 1730, # 5190 - 3936, 491, 1588, 745, 897, 2948, 843, 3340, 3937, 2757, 2870, 3273, 1768, 998, 2217, 2069, # 5206 - 397, 1826, 1195, 1969, 3659, 2993, 3341, 284, 7599, 3782, 2500, 2137, 2119, 1903, 7600, 3938, # 5222 - 2150, 3939, 4151, 1036, 3443, 1904, 114, 2559, 4152, 209, 1527, 7601, 7602, 2949, 2831, 2625, # 5238 - 2385, 2719, 3139, 812, 2560, 7603, 3274, 7604, 1559, 737, 1884, 3660, 1210, 885, 28, 2686, # 5254 - 3553, 3783, 7605, 4153, 1004, 1779, 4418, 7606, 346, 1981, 2218, 2687, 4419, 3784, 1742, 797, # 5270 - 1642, 3940, 1933, 1072, 1384, 2151, 896, 3941, 3275, 3661, 3197, 2871, 3554, 7607, 2561, 1958, # 5286 - 4420, 2450, 1785, 7608, 7609, 7610, 3942, 4154, 1005, 1308, 3662, 4155, 2720, 4421, 4422, 1528, # 5302 - 2600, 161, 1178, 4156, 1982, 987, 4423, 1101, 4157, 631, 3943, 1157, 3198, 2420, 1343, 1241, # 5318 - 1016, 2239, 2562, 372, 877, 2339, 2501, 1160, 555, 1934, 911, 3944, 7611, 466, 1170, 169, # 5334 - 1051, 2907, 2688, 3663, 2474, 2994, 1182, 2011, 2563, 1251, 2626, 7612, 992, 2340, 3444, 1540, # 5350 - 2721, 1201, 2070, 2401, 1996, 2475, 7613, 4424, 528, 1922, 2188, 1503, 1873, 1570, 2364, 3342, # 5366 - 3276, 7614, 557, 1073, 7615, 1827, 3445, 2087, 2266, 3140, 3039, 3084, 767, 3085, 2786, 4425, # 5382 - 1006, 4158, 4426, 2341, 1267, 2176, 3664, 3199, 778, 3945, 3200, 2722, 1597, 2657, 7616, 4427, # 5398 - 7617, 3446, 7618, 7619, 7620, 3277, 2689, 1433, 3278, 131, 95, 1504, 3946, 723, 4159, 3141, # 5414 - 1841, 3555, 2758, 2189, 3947, 2027, 2104, 3665, 7621, 2995, 3948, 1218, 7622, 3343, 3201, 3949, # 5430 - 4160, 2576, 248, 1634, 3785, 912, 7623, 2832, 3666, 3040, 3786, 654, 53, 7624, 2996, 7625, # 5446 - 1688, 4428, 777, 3447, 1032, 3950, 1425, 7626, 191, 820, 2120, 2833, 971, 4429, 931, 3202, # 5462 - 135, 664, 783, 3787, 1997, 772, 2908, 1935, 3951, 3788, 4430, 2909, 3203, 282, 2723, 640, # 5478 - 1372, 3448, 1127, 922, 325, 3344, 7627, 7628, 711, 2044, 7629, 7630, 3952, 2219, 2787, 1936, # 5494 - 3953, 3345, 2220, 2251, 3789, 2300, 7631, 4431, 3790, 1258, 3279, 3954, 3204, 2138, 2950, 3955, # 5510 - 3956, 7632, 2221, 258, 3205, 4432, 101, 1227, 7633, 3280, 1755, 7634, 1391, 3281, 7635, 2910, # 5526 - 2056, 893, 7636, 7637, 7638, 1402, 4161, 2342, 7639, 7640, 3206, 3556, 7641, 7642, 878, 1325, # 5542 - 1780, 2788, 4433, 259, 1385, 2577, 744, 1183, 2267, 4434, 7643, 3957, 2502, 7644, 684, 1024, # 5558 - 4162, 7645, 472, 3557, 3449, 1165, 3282, 3958, 3959, 322, 2152, 881, 455, 1695, 1152, 1340, # 5574 - 660, 554, 2153, 4435, 1058, 4436, 4163, 830, 1065, 3346, 3960, 4437, 1923, 7646, 1703, 1918, # 5590 - 7647, 932, 2268, 122, 7648, 4438, 947, 677, 7649, 3791, 2627, 297, 1905, 1924, 2269, 4439, # 5606 - 2317, 3283, 7650, 7651, 4164, 7652, 4165, 84, 4166, 112, 989, 7653, 547, 1059, 3961, 701, # 5622 - 3558, 1019, 7654, 4167, 7655, 3450, 942, 639, 457, 2301, 2451, 993, 2951, 407, 851, 494, # 5638 - 4440, 3347, 927, 7656, 1237, 7657, 2421, 3348, 573, 4168, 680, 921, 2911, 1279, 1874, 285, # 5654 - 790, 1448, 1983, 719, 2167, 7658, 7659, 4441, 3962, 3963, 1649, 7660, 1541, 563, 7661, 1077, # 5670 - 7662, 3349, 3041, 3451, 511, 2997, 3964, 3965, 3667, 3966, 1268, 2564, 3350, 3207, 4442, 4443, # 5686 - 7663, 535, 1048, 1276, 1189, 2912, 2028, 3142, 1438, 1373, 2834, 2952, 1134, 2012, 7664, 4169, # 5702 - 1238, 2578, 3086, 1259, 7665, 700, 7666, 2953, 3143, 3668, 4170, 7667, 4171, 1146, 1875, 1906, # 5718 - 4444, 2601, 3967, 781, 2422, 132, 1589, 203, 147, 273, 2789, 2402, 898, 1786, 2154, 3968, # 5734 - 3969, 7668, 3792, 2790, 7669, 7670, 4445, 4446, 7671, 3208, 7672, 1635, 3793, 965, 7673, 1804, # 5750 - 2690, 1516, 3559, 1121, 1082, 1329, 3284, 3970, 1449, 3794, 65, 1128, 2835, 2913, 2759, 1590, # 5766 - 3795, 7674, 7675, 12, 2658, 45, 976, 2579, 3144, 4447, 517, 2528, 1013, 1037, 3209, 7676, # 5782 - 3796, 2836, 7677, 3797, 7678, 3452, 7679, 2602, 614, 1998, 2318, 3798, 3087, 2724, 2628, 7680, # 5798 - 2580, 4172, 599, 1269, 7681, 1810, 3669, 7682, 2691, 3088, 759, 1060, 489, 1805, 3351, 3285, # 5814 - 1358, 7683, 7684, 2386, 1387, 1215, 2629, 2252, 490, 7685, 7686, 4173, 1759, 2387, 2343, 7687, # 5830 - 4448, 3799, 1907, 3971, 2630, 1806, 3210, 4449, 3453, 3286, 2760, 2344, 874, 7688, 7689, 3454, # 5846 - 3670, 1858, 91, 2914, 3671, 3042, 3800, 4450, 7690, 3145, 3972, 2659, 7691, 3455, 1202, 1403, # 5862 - 3801, 2954, 2529, 1517, 2503, 4451, 3456, 2504, 7692, 4452, 7693, 2692, 1885, 1495, 1731, 3973, # 5878 - 2365, 4453, 7694, 2029, 7695, 7696, 3974, 2693, 1216, 237, 2581, 4174, 2319, 3975, 3802, 4454, # 5894 - 4455, 2694, 3560, 3457, 445, 4456, 7697, 7698, 7699, 7700, 2761, 61, 3976, 3672, 1822, 3977, # 5910 - 7701, 687, 2045, 935, 925, 405, 2660, 703, 1096, 1859, 2725, 4457, 3978, 1876, 1367, 2695, # 5926 - 3352, 918, 2105, 1781, 2476, 334, 3287, 1611, 1093, 4458, 564, 3146, 3458, 3673, 3353, 945, # 5942 - 2631, 2057, 4459, 7702, 1925, 872, 4175, 7703, 3459, 2696, 3089, 349, 4176, 3674, 3979, 4460, # 5958 - 3803, 4177, 3675, 2155, 3980, 4461, 4462, 4178, 4463, 2403, 2046, 782, 3981, 400, 251, 4179, # 5974 - 1624, 7704, 7705, 277, 3676, 299, 1265, 476, 1191, 3804, 2121, 4180, 4181, 1109, 205, 7706, # 5990 - 2582, 1000, 2156, 3561, 1860, 7707, 7708, 7709, 4464, 7710, 4465, 2565, 107, 2477, 2157, 3982, # 6006 - 3460, 3147, 7711, 1533, 541, 1301, 158, 753, 4182, 2872, 3562, 7712, 1696, 370, 1088, 4183, # 6022 - 4466, 3563, 579, 327, 440, 162, 2240, 269, 1937, 1374, 3461, 968, 3043, 56, 1396, 3090, # 6038 - 2106, 3288, 3354, 7713, 1926, 2158, 4467, 2998, 7714, 3564, 7715, 7716, 3677, 4468, 2478, 7717, # 6054 - 2791, 7718, 1650, 4469, 7719, 2603, 7720, 7721, 3983, 2661, 3355, 1149, 3356, 3984, 3805, 3985, # 6070 - 7722, 1076, 49, 7723, 951, 3211, 3289, 3290, 450, 2837, 920, 7724, 1811, 2792, 2366, 4184, # 6086 - 1908, 1138, 2367, 3806, 3462, 7725, 3212, 4470, 1909, 1147, 1518, 2423, 4471, 3807, 7726, 4472, # 6102 - 2388, 2604, 260, 1795, 3213, 7727, 7728, 3808, 3291, 708, 7729, 3565, 1704, 7730, 3566, 1351, # 6118 - 1618, 3357, 2999, 1886, 944, 4185, 3358, 4186, 3044, 3359, 4187, 7731, 3678, 422, 413, 1714, # 6134 - 3292, 500, 2058, 2345, 4188, 2479, 7732, 1344, 1910, 954, 7733, 1668, 7734, 7735, 3986, 2404, # 6150 - 4189, 3567, 3809, 4190, 7736, 2302, 1318, 2505, 3091, 133, 3092, 2873, 4473, 629, 31, 2838, # 6166 - 2697, 3810, 4474, 850, 949, 4475, 3987, 2955, 1732, 2088, 4191, 1496, 1852, 7737, 3988, 620, # 6182 - 3214, 981, 1242, 3679, 3360, 1619, 3680, 1643, 3293, 2139, 2452, 1970, 1719, 3463, 2168, 7738, # 6198 - 3215, 7739, 7740, 3361, 1828, 7741, 1277, 4476, 1565, 2047, 7742, 1636, 3568, 3093, 7743, 869, # 6214 - 2839, 655, 3811, 3812, 3094, 3989, 3000, 3813, 1310, 3569, 4477, 7744, 7745, 7746, 1733, 558, # 6230 - 4478, 3681, 335, 1549, 3045, 1756, 4192, 3682, 1945, 3464, 1829, 1291, 1192, 470, 2726, 2107, # 6246 - 2793, 913, 1054, 3990, 7747, 1027, 7748, 3046, 3991, 4479, 982, 2662, 3362, 3148, 3465, 3216, # 6262 - 3217, 1946, 2794, 7749, 571, 4480, 7750, 1830, 7751, 3570, 2583, 1523, 2424, 7752, 2089, 984, # 6278 - 4481, 3683, 1959, 7753, 3684, 852, 923, 2795, 3466, 3685, 969, 1519, 999, 2048, 2320, 1705, # 6294 - 7754, 3095, 615, 1662, 151, 597, 3992, 2405, 2321, 1049, 275, 4482, 3686, 4193, 568, 3687, # 6310 - 3571, 2480, 4194, 3688, 7755, 2425, 2270, 409, 3218, 7756, 1566, 2874, 3467, 1002, 769, 2840, # 6326 - 194, 2090, 3149, 3689, 2222, 3294, 4195, 628, 1505, 7757, 7758, 1763, 2177, 3001, 3993, 521, # 6342 - 1161, 2584, 1787, 2203, 2406, 4483, 3994, 1625, 4196, 4197, 412, 42, 3096, 464, 7759, 2632, # 6358 - 4484, 3363, 1760, 1571, 2875, 3468, 2530, 1219, 2204, 3814, 2633, 2140, 2368, 4485, 4486, 3295, # 6374 - 1651, 3364, 3572, 7760, 7761, 3573, 2481, 3469, 7762, 3690, 7763, 7764, 2271, 2091, 460, 7765, # 6390 - 4487, 7766, 3002, 962, 588, 3574, 289, 3219, 2634, 1116, 52, 7767, 3047, 1796, 7768, 7769, # 6406 - 7770, 1467, 7771, 1598, 1143, 3691, 4198, 1984, 1734, 1067, 4488, 1280, 3365, 465, 4489, 1572, # 6422 - 510, 7772, 1927, 2241, 1812, 1644, 3575, 7773, 4490, 3692, 7774, 7775, 2663, 1573, 1534, 7776, # 6438 - 7777, 4199, 536, 1807, 1761, 3470, 3815, 3150, 2635, 7778, 7779, 7780, 4491, 3471, 2915, 1911, # 6454 - 2796, 7781, 3296, 1122, 377, 3220, 7782, 360, 7783, 7784, 4200, 1529, 551, 7785, 2059, 3693, # 6470 - 1769, 2426, 7786, 2916, 4201, 3297, 3097, 2322, 2108, 2030, 4492, 1404, 136, 1468, 1479, 672, # 6486 - 1171, 3221, 2303, 271, 3151, 7787, 2762, 7788, 2049, 678, 2727, 865, 1947, 4493, 7789, 2013, # 6502 - 3995, 2956, 7790, 2728, 2223, 1397, 3048, 3694, 4494, 4495, 1735, 2917, 3366, 3576, 7791, 3816, # 6518 - 509, 2841, 2453, 2876, 3817, 7792, 7793, 3152, 3153, 4496, 4202, 2531, 4497, 2304, 1166, 1010, # 6534 - 552, 681, 1887, 7794, 7795, 2957, 2958, 3996, 1287, 1596, 1861, 3154, 358, 453, 736, 175, # 6550 - 478, 1117, 905, 1167, 1097, 7796, 1853, 1530, 7797, 1706, 7798, 2178, 3472, 2287, 3695, 3473, # 6566 - 3577, 4203, 2092, 4204, 7799, 3367, 1193, 2482, 4205, 1458, 2190, 2205, 1862, 1888, 1421, 3298, # 6582 - 2918, 3049, 2179, 3474, 595, 2122, 7800, 3997, 7801, 7802, 4206, 1707, 2636, 223, 3696, 1359, # 6598 - 751, 3098, 183, 3475, 7803, 2797, 3003, 419, 2369, 633, 704, 3818, 2389, 241, 7804, 7805, # 6614 - 7806, 838, 3004, 3697, 2272, 2763, 2454, 3819, 1938, 2050, 3998, 1309, 3099, 2242, 1181, 7807, # 6630 - 1136, 2206, 3820, 2370, 1446, 4207, 2305, 4498, 7808, 7809, 4208, 1055, 2605, 484, 3698, 7810, # 6646 - 3999, 625, 4209, 2273, 3368, 1499, 4210, 4000, 7811, 4001, 4211, 3222, 2274, 2275, 3476, 7812, # 6662 - 7813, 2764, 808, 2606, 3699, 3369, 4002, 4212, 3100, 2532, 526, 3370, 3821, 4213, 955, 7814, # 6678 - 1620, 4214, 2637, 2427, 7815, 1429, 3700, 1669, 1831, 994, 928, 7816, 3578, 1260, 7817, 7818, # 6694 - 7819, 1948, 2288, 741, 2919, 1626, 4215, 2729, 2455, 867, 1184, 362, 3371, 1392, 7820, 7821, # 6710 - 4003, 4216, 1770, 1736, 3223, 2920, 4499, 4500, 1928, 2698, 1459, 1158, 7822, 3050, 3372, 2877, # 6726 - 1292, 1929, 2506, 2842, 3701, 1985, 1187, 2071, 2014, 2607, 4217, 7823, 2566, 2507, 2169, 3702, # 6742 - 2483, 3299, 7824, 3703, 4501, 7825, 7826, 666, 1003, 3005, 1022, 3579, 4218, 7827, 4502, 1813, # 6758 - 2253, 574, 3822, 1603, 295, 1535, 705, 3823, 4219, 283, 858, 417, 7828, 7829, 3224, 4503, # 6774 - 4504, 3051, 1220, 1889, 1046, 2276, 2456, 4004, 1393, 1599, 689, 2567, 388, 4220, 7830, 2484, # 6790 - 802, 7831, 2798, 3824, 2060, 1405, 2254, 7832, 4505, 3825, 2109, 1052, 1345, 3225, 1585, 7833, # 6806 - 809, 7834, 7835, 7836, 575, 2730, 3477, 956, 1552, 1469, 1144, 2323, 7837, 2324, 1560, 2457, # 6822 - 3580, 3226, 4005, 616, 2207, 3155, 2180, 2289, 7838, 1832, 7839, 3478, 4506, 7840, 1319, 3704, # 6838 - 3705, 1211, 3581, 1023, 3227, 1293, 2799, 7841, 7842, 7843, 3826, 607, 2306, 3827, 762, 2878, # 6854 - 1439, 4221, 1360, 7844, 1485, 3052, 7845, 4507, 1038, 4222, 1450, 2061, 2638, 4223, 1379, 4508, # 6870 - 2585, 7846, 7847, 4224, 1352, 1414, 2325, 2921, 1172, 7848, 7849, 3828, 3829, 7850, 1797, 1451, # 6886 - 7851, 7852, 7853, 7854, 2922, 4006, 4007, 2485, 2346, 411, 4008, 4009, 3582, 3300, 3101, 4509, # 6902 - 1561, 2664, 1452, 4010, 1375, 7855, 7856, 47, 2959, 316, 7857, 1406, 1591, 2923, 3156, 7858, # 6918 - 1025, 2141, 3102, 3157, 354, 2731, 884, 2224, 4225, 2407, 508, 3706, 726, 3583, 996, 2428, # 6934 - 3584, 729, 7859, 392, 2191, 1453, 4011, 4510, 3707, 7860, 7861, 2458, 3585, 2608, 1675, 2800, # 6950 - 919, 2347, 2960, 2348, 1270, 4511, 4012, 73, 7862, 7863, 647, 7864, 3228, 2843, 2255, 1550, # 6966 - 1346, 3006, 7865, 1332, 883, 3479, 7866, 7867, 7868, 7869, 3301, 2765, 7870, 1212, 831, 1347, # 6982 - 4226, 4512, 2326, 3830, 1863, 3053, 720, 3831, 4513, 4514, 3832, 7871, 4227, 7872, 7873, 4515, # 6998 - 7874, 7875, 1798, 4516, 3708, 2609, 4517, 3586, 1645, 2371, 7876, 7877, 2924, 669, 2208, 2665, # 7014 - 2429, 7878, 2879, 7879, 7880, 1028, 3229, 7881, 4228, 2408, 7882, 2256, 1353, 7883, 7884, 4518, # 7030 - 3158, 518, 7885, 4013, 7886, 4229, 1960, 7887, 2142, 4230, 7888, 7889, 3007, 2349, 2350, 3833, # 7046 - 516, 1833, 1454, 4014, 2699, 4231, 4519, 2225, 2610, 1971, 1129, 3587, 7890, 2766, 7891, 2961, # 7062 - 1422, 577, 1470, 3008, 1524, 3373, 7892, 7893, 432, 4232, 3054, 3480, 7894, 2586, 1455, 2508, # 7078 - 2226, 1972, 1175, 7895, 1020, 2732, 4015, 3481, 4520, 7896, 2733, 7897, 1743, 1361, 3055, 3482, # 7094 - 2639, 4016, 4233, 4521, 2290, 895, 924, 4234, 2170, 331, 2243, 3056, 166, 1627, 3057, 1098, # 7110 - 7898, 1232, 2880, 2227, 3374, 4522, 657, 403, 1196, 2372, 542, 3709, 3375, 1600, 4235, 3483, # 7126 - 7899, 4523, 2767, 3230, 576, 530, 1362, 7900, 4524, 2533, 2666, 3710, 4017, 7901, 842, 3834, # 7142 - 7902, 2801, 2031, 1014, 4018, 213, 2700, 3376, 665, 621, 4236, 7903, 3711, 2925, 2430, 7904, # 7158 - 2431, 3302, 3588, 3377, 7905, 4237, 2534, 4238, 4525, 3589, 1682, 4239, 3484, 1380, 7906, 724, # 7174 - 2277, 600, 1670, 7907, 1337, 1233, 4526, 3103, 2244, 7908, 1621, 4527, 7909, 651, 4240, 7910, # 7190 - 1612, 4241, 2611, 7911, 2844, 7912, 2734, 2307, 3058, 7913, 716, 2459, 3059, 174, 1255, 2701, # 7206 - 4019, 3590, 548, 1320, 1398, 728, 4020, 1574, 7914, 1890, 1197, 3060, 4021, 7915, 3061, 3062, # 7222 - 3712, 3591, 3713, 747, 7916, 635, 4242, 4528, 7917, 7918, 7919, 4243, 7920, 7921, 4529, 7922, # 7238 - 3378, 4530, 2432, 451, 7923, 3714, 2535, 2072, 4244, 2735, 4245, 4022, 7924, 1764, 4531, 7925, # 7254 - 4246, 350, 7926, 2278, 2390, 2486, 7927, 4247, 4023, 2245, 1434, 4024, 488, 4532, 458, 4248, # 7270 - 4025, 3715, 771, 1330, 2391, 3835, 2568, 3159, 2159, 2409, 1553, 2667, 3160, 4249, 7928, 2487, # 7286 - 2881, 2612, 1720, 2702, 4250, 3379, 4533, 7929, 2536, 4251, 7930, 3231, 4252, 2768, 7931, 2015, # 7302 - 2736, 7932, 1155, 1017, 3716, 3836, 7933, 3303, 2308, 201, 1864, 4253, 1430, 7934, 4026, 7935, # 7318 - 7936, 7937, 7938, 7939, 4254, 1604, 7940, 414, 1865, 371, 2587, 4534, 4535, 3485, 2016, 3104, # 7334 - 4536, 1708, 960, 4255, 887, 389, 2171, 1536, 1663, 1721, 7941, 2228, 4027, 2351, 2926, 1580, # 7350 - 7942, 7943, 7944, 1744, 7945, 2537, 4537, 4538, 7946, 4539, 7947, 2073, 7948, 7949, 3592, 3380, # 7366 - 2882, 4256, 7950, 4257, 2640, 3381, 2802, 673, 2703, 2460, 709, 3486, 4028, 3593, 4258, 7951, # 7382 - 1148, 502, 634, 7952, 7953, 1204, 4540, 3594, 1575, 4541, 2613, 3717, 7954, 3718, 3105, 948, # 7398 - 3232, 121, 1745, 3837, 1110, 7955, 4259, 3063, 2509, 3009, 4029, 3719, 1151, 1771, 3838, 1488, # 7414 - 4030, 1986, 7956, 2433, 3487, 7957, 7958, 2093, 7959, 4260, 3839, 1213, 1407, 2803, 531, 2737, # 7430 - 2538, 3233, 1011, 1537, 7960, 2769, 4261, 3106, 1061, 7961, 3720, 3721, 1866, 2883, 7962, 2017, # 7446 - 120, 4262, 4263, 2062, 3595, 3234, 2309, 3840, 2668, 3382, 1954, 4542, 7963, 7964, 3488, 1047, # 7462 - 2704, 1266, 7965, 1368, 4543, 2845, 649, 3383, 3841, 2539, 2738, 1102, 2846, 2669, 7966, 7967, # 7478 - 1999, 7968, 1111, 3596, 2962, 7969, 2488, 3842, 3597, 2804, 1854, 3384, 3722, 7970, 7971, 3385, # 7494 - 2410, 2884, 3304, 3235, 3598, 7972, 2569, 7973, 3599, 2805, 4031, 1460, 856, 7974, 3600, 7975, # 7510 - 2885, 2963, 7976, 2886, 3843, 7977, 4264, 632, 2510, 875, 3844, 1697, 3845, 2291, 7978, 7979, # 7526 - 4544, 3010, 1239, 580, 4545, 4265, 7980, 914, 936, 2074, 1190, 4032, 1039, 2123, 7981, 7982, # 7542 - 7983, 3386, 1473, 7984, 1354, 4266, 3846, 7985, 2172, 3064, 4033, 915, 3305, 4267, 4268, 3306, # 7558 - 1605, 1834, 7986, 2739, 398, 3601, 4269, 3847, 4034, 328, 1912, 2847, 4035, 3848, 1331, 4270, # 7574 - 3011, 937, 4271, 7987, 3602, 4036, 4037, 3387, 2160, 4546, 3388, 524, 742, 538, 3065, 1012, # 7590 - 7988, 7989, 3849, 2461, 7990, 658, 1103, 225, 3850, 7991, 7992, 4547, 7993, 4548, 7994, 3236, # 7606 - 1243, 7995, 4038, 963, 2246, 4549, 7996, 2705, 3603, 3161, 7997, 7998, 2588, 2327, 7999, 4550, # 7622 - 8000, 8001, 8002, 3489, 3307, 957, 3389, 2540, 2032, 1930, 2927, 2462, 870, 2018, 3604, 1746, # 7638 - 2770, 2771, 2434, 2463, 8003, 3851, 8004, 3723, 3107, 3724, 3490, 3390, 3725, 8005, 1179, 3066, # 7654 - 8006, 3162, 2373, 4272, 3726, 2541, 3163, 3108, 2740, 4039, 8007, 3391, 1556, 2542, 2292, 977, # 7670 - 2887, 2033, 4040, 1205, 3392, 8008, 1765, 3393, 3164, 2124, 1271, 1689, 714, 4551, 3491, 8009, # 7686 - 2328, 3852, 533, 4273, 3605, 2181, 617, 8010, 2464, 3308, 3492, 2310, 8011, 8012, 3165, 8013, # 7702 - 8014, 3853, 1987, 618, 427, 2641, 3493, 3394, 8015, 8016, 1244, 1690, 8017, 2806, 4274, 4552, # 7718 - 8018, 3494, 8019, 8020, 2279, 1576, 473, 3606, 4275, 3395, 972, 8021, 3607, 8022, 3067, 8023, # 7734 - 8024, 4553, 4554, 8025, 3727, 4041, 4042, 8026, 153, 4555, 356, 8027, 1891, 2888, 4276, 2143, # 7750 - 408, 803, 2352, 8028, 3854, 8029, 4277, 1646, 2570, 2511, 4556, 4557, 3855, 8030, 3856, 4278, # 7766 - 8031, 2411, 3396, 752, 8032, 8033, 1961, 2964, 8034, 746, 3012, 2465, 8035, 4279, 3728, 698, # 7782 - 4558, 1892, 4280, 3608, 2543, 4559, 3609, 3857, 8036, 3166, 3397, 8037, 1823, 1302, 4043, 2706, # 7798 - 3858, 1973, 4281, 8038, 4282, 3167, 823, 1303, 1288, 1236, 2848, 3495, 4044, 3398, 774, 3859, # 7814 - 8039, 1581, 4560, 1304, 2849, 3860, 4561, 8040, 2435, 2161, 1083, 3237, 4283, 4045, 4284, 344, # 7830 - 1173, 288, 2311, 454, 1683, 8041, 8042, 1461, 4562, 4046, 2589, 8043, 8044, 4563, 985, 894, # 7846 - 8045, 3399, 3168, 8046, 1913, 2928, 3729, 1988, 8047, 2110, 1974, 8048, 4047, 8049, 2571, 1194, # 7862 - 425, 8050, 4564, 3169, 1245, 3730, 4285, 8051, 8052, 2850, 8053, 636, 4565, 1855, 3861, 760, # 7878 - 1799, 8054, 4286, 2209, 1508, 4566, 4048, 1893, 1684, 2293, 8055, 8056, 8057, 4287, 4288, 2210, # 7894 - 479, 8058, 8059, 832, 8060, 4049, 2489, 8061, 2965, 2490, 3731, 990, 3109, 627, 1814, 2642, # 7910 - 4289, 1582, 4290, 2125, 2111, 3496, 4567, 8062, 799, 4291, 3170, 8063, 4568, 2112, 1737, 3013, # 7926 - 1018, 543, 754, 4292, 3309, 1676, 4569, 4570, 4050, 8064, 1489, 8065, 3497, 8066, 2614, 2889, # 7942 - 4051, 8067, 8068, 2966, 8069, 8070, 8071, 8072, 3171, 4571, 4572, 2182, 1722, 8073, 3238, 3239, # 7958 - 1842, 3610, 1715, 481, 365, 1975, 1856, 8074, 8075, 1962, 2491, 4573, 8076, 2126, 3611, 3240, # 7974 - 433, 1894, 2063, 2075, 8077, 602, 2741, 8078, 8079, 8080, 8081, 8082, 3014, 1628, 3400, 8083, # 7990 - 3172, 4574, 4052, 2890, 4575, 2512, 8084, 2544, 2772, 8085, 8086, 8087, 3310, 4576, 2891, 8088, # 8006 - 4577, 8089, 2851, 4578, 4579, 1221, 2967, 4053, 2513, 8090, 8091, 8092, 1867, 1989, 8093, 8094, # 8022 - 8095, 1895, 8096, 8097, 4580, 1896, 4054, 318, 8098, 2094, 4055, 4293, 8099, 8100, 485, 8101, # 8038 - 938, 3862, 553, 2670, 116, 8102, 3863, 3612, 8103, 3498, 2671, 2773, 3401, 3311, 2807, 8104, # 8054 - 3613, 2929, 4056, 1747, 2930, 2968, 8105, 8106, 207, 8107, 8108, 2672, 4581, 2514, 8109, 3015, # 8070 - 890, 3614, 3864, 8110, 1877, 3732, 3402, 8111, 2183, 2353, 3403, 1652, 8112, 8113, 8114, 941, # 8086 - 2294, 208, 3499, 4057, 2019, 330, 4294, 3865, 2892, 2492, 3733, 4295, 8115, 8116, 8117, 8118, # 8102 -) -# fmt: on diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euctwprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euctwprober.py deleted file mode 100644 index a37ab18..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/euctwprober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .chardistribution import EUCTWDistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import EUCTW_SM_MODEL - - -class EUCTWProber(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(EUCTW_SM_MODEL) - self.distribution_analyzer = EUCTWDistributionAnalysis() - self.reset() - - @property - def charset_name(self) -> str: - return "EUC-TW" - - @property - def language(self) -> str: - return "Taiwan" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/gb2312freq.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/gb2312freq.py deleted file mode 100644 index b32bfc7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/gb2312freq.py +++ /dev/null @@ -1,284 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# GB2312 most frequently used character table -# -# Char to FreqOrder table , from hz6763 - -# 512 --> 0.79 -- 0.79 -# 1024 --> 0.92 -- 0.13 -# 2048 --> 0.98 -- 0.06 -# 6768 --> 1.00 -- 0.02 -# -# Ideal Distribution Ratio = 0.79135/(1-0.79135) = 3.79 -# Random Distribution Ration = 512 / (3755 - 512) = 0.157 -# -# Typical Distribution Ratio about 25% of Ideal one, still much higher that RDR - -GB2312_TYPICAL_DISTRIBUTION_RATIO = 0.9 - -GB2312_TABLE_SIZE = 3760 - -# fmt: off -GB2312_CHAR_TO_FREQ_ORDER = ( -1671, 749,1443,2364,3924,3807,2330,3921,1704,3463,2691,1511,1515, 572,3191,2205, -2361, 224,2558, 479,1711, 963,3162, 440,4060,1905,2966,2947,3580,2647,3961,3842, -2204, 869,4207, 970,2678,5626,2944,2956,1479,4048, 514,3595, 588,1346,2820,3409, - 249,4088,1746,1873,2047,1774, 581,1813, 358,1174,3590,1014,1561,4844,2245, 670, -1636,3112, 889,1286, 953, 556,2327,3060,1290,3141, 613, 185,3477,1367, 850,3820, -1715,2428,2642,2303,2732,3041,2562,2648,3566,3946,1349, 388,3098,2091,1360,3585, - 152,1687,1539, 738,1559, 59,1232,2925,2267,1388,1249,1741,1679,2960, 151,1566, -1125,1352,4271, 924,4296, 385,3166,4459, 310,1245,2850, 70,3285,2729,3534,3575, -2398,3298,3466,1960,2265, 217,3647, 864,1909,2084,4401,2773,1010,3269,5152, 853, -3051,3121,1244,4251,1895, 364,1499,1540,2313,1180,3655,2268, 562, 715,2417,3061, - 544, 336,3768,2380,1752,4075, 950, 280,2425,4382, 183,2759,3272, 333,4297,2155, -1688,2356,1444,1039,4540, 736,1177,3349,2443,2368,2144,2225, 565, 196,1482,3406, - 927,1335,4147, 692, 878,1311,1653,3911,3622,1378,4200,1840,2969,3149,2126,1816, -2534,1546,2393,2760, 737,2494, 13, 447, 245,2747, 38,2765,2129,2589,1079, 606, - 360, 471,3755,2890, 404, 848, 699,1785,1236, 370,2221,1023,3746,2074,2026,2023, -2388,1581,2119, 812,1141,3091,2536,1519, 804,2053, 406,1596,1090, 784, 548,4414, -1806,2264,2936,1100, 343,4114,5096, 622,3358, 743,3668,1510,1626,5020,3567,2513, -3195,4115,5627,2489,2991, 24,2065,2697,1087,2719, 48,1634, 315, 68, 985,2052, - 198,2239,1347,1107,1439, 597,2366,2172, 871,3307, 919,2487,2790,1867, 236,2570, -1413,3794, 906,3365,3381,1701,1982,1818,1524,2924,1205, 616,2586,2072,2004, 575, - 253,3099, 32,1365,1182, 197,1714,2454,1201, 554,3388,3224,2748, 756,2587, 250, -2567,1507,1517,3529,1922,2761,2337,3416,1961,1677,2452,2238,3153, 615, 911,1506, -1474,2495,1265,1906,2749,3756,3280,2161, 898,2714,1759,3450,2243,2444, 563, 26, -3286,2266,3769,3344,2707,3677, 611,1402, 531,1028,2871,4548,1375, 261,2948, 835, -1190,4134, 353, 840,2684,1900,3082,1435,2109,1207,1674, 329,1872,2781,4055,2686, -2104, 608,3318,2423,2957,2768,1108,3739,3512,3271,3985,2203,1771,3520,1418,2054, -1681,1153, 225,1627,2929, 162,2050,2511,3687,1954, 124,1859,2431,1684,3032,2894, - 585,4805,3969,2869,2704,2088,2032,2095,3656,2635,4362,2209, 256, 518,2042,2105, -3777,3657, 643,2298,1148,1779, 190, 989,3544, 414, 11,2135,2063,2979,1471, 403, -3678, 126, 770,1563, 671,2499,3216,2877, 600,1179, 307,2805,4937,1268,1297,2694, - 252,4032,1448,1494,1331,1394, 127,2256, 222,1647,1035,1481,3056,1915,1048, 873, -3651, 210, 33,1608,2516, 200,1520, 415, 102, 0,3389,1287, 817, 91,3299,2940, - 836,1814, 549,2197,1396,1669,2987,3582,2297,2848,4528,1070, 687, 20,1819, 121, -1552,1364,1461,1968,2617,3540,2824,2083, 177, 948,4938,2291, 110,4549,2066, 648, -3359,1755,2110,2114,4642,4845,1693,3937,3308,1257,1869,2123, 208,1804,3159,2992, -2531,2549,3361,2418,1350,2347,2800,2568,1291,2036,2680, 72, 842,1990, 212,1233, -1154,1586, 75,2027,3410,4900,1823,1337,2710,2676, 728,2810,1522,3026,4995, 157, - 755,1050,4022, 710, 785,1936,2194,2085,1406,2777,2400, 150,1250,4049,1206, 807, -1910, 534, 529,3309,1721,1660, 274, 39,2827, 661,2670,1578, 925,3248,3815,1094, -4278,4901,4252, 41,1150,3747,2572,2227,4501,3658,4902,3813,3357,3617,2884,2258, - 887, 538,4187,3199,1294,2439,3042,2329,2343,2497,1255, 107, 543,1527, 521,3478, -3568, 194,5062, 15, 961,3870,1241,1192,2664, 66,5215,3260,2111,1295,1127,2152, -3805,4135, 901,1164,1976, 398,1278, 530,1460, 748, 904,1054,1966,1426, 53,2909, - 509, 523,2279,1534, 536,1019, 239,1685, 460,2353, 673,1065,2401,3600,4298,2272, -1272,2363, 284,1753,3679,4064,1695, 81, 815,2677,2757,2731,1386, 859, 500,4221, -2190,2566, 757,1006,2519,2068,1166,1455, 337,2654,3203,1863,1682,1914,3025,1252, -1409,1366, 847, 714,2834,2038,3209, 964,2970,1901, 885,2553,1078,1756,3049, 301, -1572,3326, 688,2130,1996,2429,1805,1648,2930,3421,2750,3652,3088, 262,1158,1254, - 389,1641,1812, 526,1719, 923,2073,1073,1902, 468, 489,4625,1140, 857,2375,3070, -3319,2863, 380, 116,1328,2693,1161,2244, 273,1212,1884,2769,3011,1775,1142, 461, -3066,1200,2147,2212, 790, 702,2695,4222,1601,1058, 434,2338,5153,3640, 67,2360, -4099,2502, 618,3472,1329, 416,1132, 830,2782,1807,2653,3211,3510,1662, 192,2124, - 296,3979,1739,1611,3684, 23, 118, 324, 446,1239,1225, 293,2520,3814,3795,2535, -3116, 17,1074, 467,2692,2201, 387,2922, 45,1326,3055,1645,3659,2817, 958, 243, -1903,2320,1339,2825,1784,3289, 356, 576, 865,2315,2381,3377,3916,1088,3122,1713, -1655, 935, 628,4689,1034,1327, 441, 800, 720, 894,1979,2183,1528,5289,2702,1071, -4046,3572,2399,1571,3281, 79, 761,1103, 327, 134, 758,1899,1371,1615, 879, 442, - 215,2605,2579, 173,2048,2485,1057,2975,3317,1097,2253,3801,4263,1403,1650,2946, - 814,4968,3487,1548,2644,1567,1285, 2, 295,2636, 97, 946,3576, 832, 141,4257, -3273, 760,3821,3521,3156,2607, 949,1024,1733,1516,1803,1920,2125,2283,2665,3180, -1501,2064,3560,2171,1592, 803,3518,1416, 732,3897,4258,1363,1362,2458, 119,1427, - 602,1525,2608,1605,1639,3175, 694,3064, 10, 465, 76,2000,4846,4208, 444,3781, -1619,3353,2206,1273,3796, 740,2483, 320,1723,2377,3660,2619,1359,1137,1762,1724, -2345,2842,1850,1862, 912, 821,1866, 612,2625,1735,2573,3369,1093, 844, 89, 937, - 930,1424,3564,2413,2972,1004,3046,3019,2011, 711,3171,1452,4178, 428, 801,1943, - 432, 445,2811, 206,4136,1472, 730, 349, 73, 397,2802,2547, 998,1637,1167, 789, - 396,3217, 154,1218, 716,1120,1780,2819,4826,1931,3334,3762,2139,1215,2627, 552, -3664,3628,3232,1405,2383,3111,1356,2652,3577,3320,3101,1703, 640,1045,1370,1246, -4996, 371,1575,2436,1621,2210, 984,4033,1734,2638, 16,4529, 663,2755,3255,1451, -3917,2257,1253,1955,2234,1263,2951, 214,1229, 617, 485, 359,1831,1969, 473,2310, - 750,2058, 165, 80,2864,2419, 361,4344,2416,2479,1134, 796,3726,1266,2943, 860, -2715, 938, 390,2734,1313,1384, 248, 202, 877,1064,2854, 522,3907, 279,1602, 297, -2357, 395,3740, 137,2075, 944,4089,2584,1267,3802, 62,1533,2285, 178, 176, 780, -2440, 201,3707, 590, 478,1560,4354,2117,1075, 30, 74,4643,4004,1635,1441,2745, - 776,2596, 238,1077,1692,1912,2844, 605, 499,1742,3947, 241,3053, 980,1749, 936, -2640,4511,2582, 515,1543,2162,5322,2892,2993, 890,2148,1924, 665,1827,3581,1032, - 968,3163, 339,1044,1896, 270, 583,1791,1720,4367,1194,3488,3669, 43,2523,1657, - 163,2167, 290,1209,1622,3378, 550, 634,2508,2510, 695,2634,2384,2512,1476,1414, - 220,1469,2341,2138,2852,3183,2900,4939,2865,3502,1211,3680, 854,3227,1299,2976, -3172, 186,2998,1459, 443,1067,3251,1495, 321,1932,3054, 909, 753,1410,1828, 436, -2441,1119,1587,3164,2186,1258, 227, 231,1425,1890,3200,3942, 247, 959, 725,5254, -2741, 577,2158,2079, 929, 120, 174, 838,2813, 591,1115, 417,2024, 40,3240,1536, -1037, 291,4151,2354, 632,1298,2406,2500,3535,1825,1846,3451, 205,1171, 345,4238, - 18,1163, 811, 685,2208,1217, 425,1312,1508,1175,4308,2552,1033, 587,1381,3059, -2984,3482, 340,1316,4023,3972, 792,3176, 519, 777,4690, 918, 933,4130,2981,3741, - 90,3360,2911,2200,5184,4550, 609,3079,2030, 272,3379,2736, 363,3881,1130,1447, - 286, 779, 357,1169,3350,3137,1630,1220,2687,2391, 747,1277,3688,2618,2682,2601, -1156,3196,5290,4034,3102,1689,3596,3128, 874, 219,2783, 798, 508,1843,2461, 269, -1658,1776,1392,1913,2983,3287,2866,2159,2372, 829,4076, 46,4253,2873,1889,1894, - 915,1834,1631,2181,2318, 298, 664,2818,3555,2735, 954,3228,3117, 527,3511,2173, - 681,2712,3033,2247,2346,3467,1652, 155,2164,3382, 113,1994, 450, 899, 494, 994, -1237,2958,1875,2336,1926,3727, 545,1577,1550, 633,3473, 204,1305,3072,2410,1956, -2471, 707,2134, 841,2195,2196,2663,3843,1026,4940, 990,3252,4997, 368,1092, 437, -3212,3258,1933,1829, 675,2977,2893, 412, 943,3723,4644,3294,3283,2230,2373,5154, -2389,2241,2661,2323,1404,2524, 593, 787, 677,3008,1275,2059, 438,2709,2609,2240, -2269,2246,1446, 36,1568,1373,3892,1574,2301,1456,3962, 693,2276,5216,2035,1143, -2720,1919,1797,1811,2763,4137,2597,1830,1699,1488,1198,2090, 424,1694, 312,3634, -3390,4179,3335,2252,1214, 561,1059,3243,2295,2561, 975,5155,2321,2751,3772, 472, -1537,3282,3398,1047,2077,2348,2878,1323,3340,3076, 690,2906, 51, 369, 170,3541, -1060,2187,2688,3670,2541,1083,1683, 928,3918, 459, 109,4427, 599,3744,4286, 143, -2101,2730,2490, 82,1588,3036,2121, 281,1860, 477,4035,1238,2812,3020,2716,3312, -1530,2188,2055,1317, 843, 636,1808,1173,3495, 649, 181,1002, 147,3641,1159,2414, -3750,2289,2795, 813,3123,2610,1136,4368, 5,3391,4541,2174, 420, 429,1728, 754, -1228,2115,2219, 347,2223,2733, 735,1518,3003,2355,3134,1764,3948,3329,1888,2424, -1001,1234,1972,3321,3363,1672,1021,1450,1584, 226, 765, 655,2526,3404,3244,2302, -3665, 731, 594,2184, 319,1576, 621, 658,2656,4299,2099,3864,1279,2071,2598,2739, - 795,3086,3699,3908,1707,2352,2402,1382,3136,2475,1465,4847,3496,3865,1085,3004, -2591,1084, 213,2287,1963,3565,2250, 822, 793,4574,3187,1772,1789,3050, 595,1484, -1959,2770,1080,2650, 456, 422,2996, 940,3322,4328,4345,3092,2742, 965,2784, 739, -4124, 952,1358,2498,2949,2565, 332,2698,2378, 660,2260,2473,4194,3856,2919, 535, -1260,2651,1208,1428,1300,1949,1303,2942, 433,2455,2450,1251,1946, 614,1269, 641, -1306,1810,2737,3078,2912, 564,2365,1419,1415,1497,4460,2367,2185,1379,3005,1307, -3218,2175,1897,3063, 682,1157,4040,4005,1712,1160,1941,1399, 394, 402,2952,1573, -1151,2986,2404, 862, 299,2033,1489,3006, 346, 171,2886,3401,1726,2932, 168,2533, - 47,2507,1030,3735,1145,3370,1395,1318,1579,3609,4560,2857,4116,1457,2529,1965, - 504,1036,2690,2988,2405, 745,5871, 849,2397,2056,3081, 863,2359,3857,2096, 99, -1397,1769,2300,4428,1643,3455,1978,1757,3718,1440, 35,4879,3742,1296,4228,2280, - 160,5063,1599,2013, 166, 520,3479,1646,3345,3012, 490,1937,1545,1264,2182,2505, -1096,1188,1369,1436,2421,1667,2792,2460,1270,2122, 727,3167,2143, 806,1706,1012, -1800,3037, 960,2218,1882, 805, 139,2456,1139,1521, 851,1052,3093,3089, 342,2039, - 744,5097,1468,1502,1585,2087, 223, 939, 326,2140,2577, 892,2481,1623,4077, 982, -3708, 135,2131, 87,2503,3114,2326,1106, 876,1616, 547,2997,2831,2093,3441,4530, -4314, 9,3256,4229,4148, 659,1462,1986,1710,2046,2913,2231,4090,4880,5255,3392, -3274,1368,3689,4645,1477, 705,3384,3635,1068,1529,2941,1458,3782,1509, 100,1656, -2548, 718,2339, 408,1590,2780,3548,1838,4117,3719,1345,3530, 717,3442,2778,3220, -2898,1892,4590,3614,3371,2043,1998,1224,3483, 891, 635, 584,2559,3355, 733,1766, -1729,1172,3789,1891,2307, 781,2982,2271,1957,1580,5773,2633,2005,4195,3097,1535, -3213,1189,1934,5693,3262, 586,3118,1324,1598, 517,1564,2217,1868,1893,4445,3728, -2703,3139,1526,1787,1992,3882,2875,1549,1199,1056,2224,1904,2711,5098,4287, 338, -1993,3129,3489,2689,1809,2815,1997, 957,1855,3898,2550,3275,3057,1105,1319, 627, -1505,1911,1883,3526, 698,3629,3456,1833,1431, 746, 77,1261,2017,2296,1977,1885, - 125,1334,1600, 525,1798,1109,2222,1470,1945, 559,2236,1186,3443,2476,1929,1411, -2411,3135,1777,3372,2621,1841,1613,3229, 668,1430,1839,2643,2916, 195,1989,2671, -2358,1387, 629,3205,2293,5256,4439, 123,1310, 888,1879,4300,3021,3605,1003,1162, -3192,2910,2010, 140,2395,2859, 55,1082,2012,2901, 662, 419,2081,1438, 680,2774, -4654,3912,1620,1731,1625,5035,4065,2328, 512,1344, 802,5443,2163,2311,2537, 524, -3399, 98,1155,2103,1918,2606,3925,2816,1393,2465,1504,3773,2177,3963,1478,4346, - 180,1113,4655,3461,2028,1698, 833,2696,1235,1322,1594,4408,3623,3013,3225,2040, -3022, 541,2881, 607,3632,2029,1665,1219, 639,1385,1686,1099,2803,3231,1938,3188, -2858, 427, 676,2772,1168,2025, 454,3253,2486,3556, 230,1950, 580, 791,1991,1280, -1086,1974,2034, 630, 257,3338,2788,4903,1017, 86,4790, 966,2789,1995,1696,1131, - 259,3095,4188,1308, 179,1463,5257, 289,4107,1248, 42,3413,1725,2288, 896,1947, - 774,4474,4254, 604,3430,4264, 392,2514,2588, 452, 237,1408,3018, 988,4531,1970, -3034,3310, 540,2370,1562,1288,2990, 502,4765,1147, 4,1853,2708, 207, 294,2814, -4078,2902,2509, 684, 34,3105,3532,2551, 644, 709,2801,2344, 573,1727,3573,3557, -2021,1081,3100,4315,2100,3681, 199,2263,1837,2385, 146,3484,1195,2776,3949, 997, -1939,3973,1008,1091,1202,1962,1847,1149,4209,5444,1076, 493, 117,5400,2521, 972, -1490,2934,1796,4542,2374,1512,2933,2657, 413,2888,1135,2762,2314,2156,1355,2369, - 766,2007,2527,2170,3124,2491,2593,2632,4757,2437, 234,3125,3591,1898,1750,1376, -1942,3468,3138, 570,2127,2145,3276,4131, 962, 132,1445,4196, 19, 941,3624,3480, -3366,1973,1374,4461,3431,2629, 283,2415,2275, 808,2887,3620,2112,2563,1353,3610, - 955,1089,3103,1053, 96, 88,4097, 823,3808,1583, 399, 292,4091,3313, 421,1128, - 642,4006, 903,2539,1877,2082, 596, 29,4066,1790, 722,2157, 130, 995,1569, 769, -1485, 464, 513,2213, 288,1923,1101,2453,4316, 133, 486,2445, 50, 625, 487,2207, - 57, 423, 481,2962, 159,3729,1558, 491, 303, 482, 501, 240,2837, 112,3648,2392, -1783, 362, 8,3433,3422, 610,2793,3277,1390,1284,1654, 21,3823, 734, 367, 623, - 193, 287, 374,1009,1483, 816, 476, 313,2255,2340,1262,2150,2899,1146,2581, 782, -2116,1659,2018,1880, 255,3586,3314,1110,2867,2137,2564, 986,2767,5185,2006, 650, - 158, 926, 762, 881,3157,2717,2362,3587, 306,3690,3245,1542,3077,2427,1691,2478, -2118,2985,3490,2438, 539,2305, 983, 129,1754, 355,4201,2386, 827,2923, 104,1773, -2838,2771, 411,2905,3919, 376, 767, 122,1114, 828,2422,1817,3506, 266,3460,1007, -1609,4998, 945,2612,4429,2274, 726,1247,1964,2914,2199,2070,4002,4108, 657,3323, -1422, 579, 455,2764,4737,1222,2895,1670, 824,1223,1487,2525, 558, 861,3080, 598, -2659,2515,1967, 752,2583,2376,2214,4180, 977, 704,2464,4999,2622,4109,1210,2961, - 819,1541, 142,2284, 44, 418, 457,1126,3730,4347,4626,1644,1876,3671,1864, 302, -1063,5694, 624, 723,1984,3745,1314,1676,2488,1610,1449,3558,3569,2166,2098, 409, -1011,2325,3704,2306, 818,1732,1383,1824,1844,3757, 999,2705,3497,1216,1423,2683, -2426,2954,2501,2726,2229,1475,2554,5064,1971,1794,1666,2014,1343, 783, 724, 191, -2434,1354,2220,5065,1763,2752,2472,4152, 131, 175,2885,3434, 92,1466,4920,2616, -3871,3872,3866, 128,1551,1632, 669,1854,3682,4691,4125,1230, 188,2973,3290,1302, -1213, 560,3266, 917, 763,3909,3249,1760, 868,1958, 764,1782,2097, 145,2277,3774, -4462, 64,1491,3062, 971,2132,3606,2442, 221,1226,1617, 218, 323,1185,3207,3147, - 571, 619,1473,1005,1744,2281, 449,1887,2396,3685, 275, 375,3816,1743,3844,3731, - 845,1983,2350,4210,1377, 773, 967,3499,3052,3743,2725,4007,1697,1022,3943,1464, -3264,2855,2722,1952,1029,2839,2467, 84,4383,2215, 820,1391,2015,2448,3672, 377, -1948,2168, 797,2545,3536,2578,2645, 94,2874,1678, 405,1259,3071, 771, 546,1315, - 470,1243,3083, 895,2468, 981, 969,2037, 846,4181, 653,1276,2928, 14,2594, 557, -3007,2474, 156, 902,1338,1740,2574, 537,2518, 973,2282,2216,2433,1928, 138,2903, -1293,2631,1612, 646,3457, 839,2935, 111, 496,2191,2847, 589,3186, 149,3994,2060, -4031,2641,4067,3145,1870, 37,3597,2136,1025,2051,3009,3383,3549,1121,1016,3261, -1301, 251,2446,2599,2153, 872,3246, 637, 334,3705, 831, 884, 921,3065,3140,4092, -2198,1944, 246,2964, 108,2045,1152,1921,2308,1031, 203,3173,4170,1907,3890, 810, -1401,2003,1690, 506, 647,1242,2828,1761,1649,3208,2249,1589,3709,2931,5156,1708, - 498, 666,2613, 834,3817,1231, 184,2851,1124, 883,3197,2261,3710,1765,1553,2658, -1178,2639,2351, 93,1193, 942,2538,2141,4402, 235,1821, 870,1591,2192,1709,1871, -3341,1618,4126,2595,2334, 603, 651, 69, 701, 268,2662,3411,2555,1380,1606, 503, - 448, 254,2371,2646, 574,1187,2309,1770, 322,2235,1292,1801, 305, 566,1133, 229, -2067,2057, 706, 167, 483,2002,2672,3295,1820,3561,3067, 316, 378,2746,3452,1112, - 136,1981, 507,1651,2917,1117, 285,4591, 182,2580,3522,1304, 335,3303,1835,2504, -1795,1792,2248, 674,1018,2106,2449,1857,2292,2845, 976,3047,1781,2600,2727,1389, -1281, 52,3152, 153, 265,3950, 672,3485,3951,4463, 430,1183, 365, 278,2169, 27, -1407,1336,2304, 209,1340,1730,2202,1852,2403,2883, 979,1737,1062, 631,2829,2542, -3876,2592, 825,2086,2226,3048,3625, 352,1417,3724, 542, 991, 431,1351,3938,1861, -2294, 826,1361,2927,3142,3503,1738, 463,2462,2723, 582,1916,1595,2808, 400,3845, -3891,2868,3621,2254, 58,2492,1123, 910,2160,2614,1372,1603,1196,1072,3385,1700, -3267,1980, 696, 480,2430, 920, 799,1570,2920,1951,2041,4047,2540,1321,4223,2469, -3562,2228,1271,2602, 401,2833,3351,2575,5157, 907,2312,1256, 410, 263,3507,1582, - 996, 678,1849,2316,1480, 908,3545,2237, 703,2322, 667,1826,2849,1531,2604,2999, -2407,3146,2151,2630,1786,3711, 469,3542, 497,3899,2409, 858, 837,4446,3393,1274, - 786, 620,1845,2001,3311, 484, 308,3367,1204,1815,3691,2332,1532,2557,1842,2020, -2724,1927,2333,4440, 567, 22,1673,2728,4475,1987,1858,1144,1597, 101,1832,3601, - 12, 974,3783,4391, 951,1412, 1,3720, 453,4608,4041, 528,1041,1027,3230,2628, -1129, 875,1051,3291,1203,2262,1069,2860,2799,2149,2615,3278, 144,1758,3040, 31, - 475,1680, 366,2685,3184, 311,1642,4008,2466,5036,1593,1493,2809, 216,1420,1668, - 233, 304,2128,3284, 232,1429,1768,1040,2008,3407,2740,2967,2543, 242,2133, 778, -1565,2022,2620, 505,2189,2756,1098,2273, 372,1614, 708, 553,2846,2094,2278, 169, -3626,2835,4161, 228,2674,3165, 809,1454,1309, 466,1705,1095, 900,3423, 880,2667, -3751,5258,2317,3109,2571,4317,2766,1503,1342, 866,4447,1118, 63,2076, 314,1881, -1348,1061, 172, 978,3515,1747, 532, 511,3970, 6, 601, 905,2699,3300,1751, 276, -1467,3725,2668, 65,4239,2544,2779,2556,1604, 578,2451,1802, 992,2331,2624,1320, -3446, 713,1513,1013, 103,2786,2447,1661, 886,1702, 916, 654,3574,2031,1556, 751, -2178,2821,2179,1498,1538,2176, 271, 914,2251,2080,1325, 638,1953,2937,3877,2432, -2754, 95,3265,1716, 260,1227,4083, 775, 106,1357,3254, 426,1607, 555,2480, 772, -1985, 244,2546, 474, 495,1046,2611,1851,2061, 71,2089,1675,2590, 742,3758,2843, -3222,1433, 267,2180,2576,2826,2233,2092,3913,2435, 956,1745,3075, 856,2113,1116, - 451, 3,1988,2896,1398, 993,2463,1878,2049,1341,2718,2721,2870,2108, 712,2904, -4363,2753,2324, 277,2872,2349,2649, 384, 987, 435, 691,3000, 922, 164,3939, 652, -1500,1184,4153,2482,3373,2165,4848,2335,3775,3508,3154,2806,2830,1554,2102,1664, -2530,1434,2408, 893,1547,2623,3447,2832,2242,2532,3169,2856,3223,2078, 49,3770, -3469, 462, 318, 656,2259,3250,3069, 679,1629,2758, 344,1138,1104,3120,1836,1283, -3115,2154,1437,4448, 934, 759,1999, 794,2862,1038, 533,2560,1722,2342, 855,2626, -1197,1663,4476,3127, 85,4240,2528, 25,1111,1181,3673, 407,3470,4561,2679,2713, - 768,1925,2841,3986,1544,1165, 932, 373,1240,2146,1930,2673, 721,4766, 354,4333, - 391,2963, 187, 61,3364,1442,1102, 330,1940,1767, 341,3809,4118, 393,2496,2062, -2211, 105, 331, 300, 439, 913,1332, 626, 379,3304,1557, 328, 689,3952, 309,1555, - 931, 317,2517,3027, 325, 569, 686,2107,3084, 60,1042,1333,2794, 264,3177,4014, -1628, 258,3712, 7,4464,1176,1043,1778, 683, 114,1975, 78,1492, 383,1886, 510, - 386, 645,5291,2891,2069,3305,4138,3867,2939,2603,2493,1935,1066,1848,3588,1015, -1282,1289,4609, 697,1453,3044,2666,3611,1856,2412, 54, 719,1330, 568,3778,2459, -1748, 788, 492, 551,1191,1000, 488,3394,3763, 282,1799, 348,2016,1523,3155,2390, -1049, 382,2019,1788,1170, 729,2968,3523, 897,3926,2785,2938,3292, 350,2319,3238, -1718,1717,2655,3453,3143,4465, 161,2889,2980,2009,1421, 56,1908,1640,2387,2232, -1917,1874,2477,4921, 148, 83,3438, 592,4245,2882,1822,1055, 741, 115,1496,1624, - 381,1638,4592,1020, 516,3214, 458, 947,4575,1432, 211,1514,2926,1865,2142, 189, - 852,1221,1400,1486, 882,2299,4036, 351, 28,1122, 700,6479,6480,6481,6482,6483, #last 512 -) -# fmt: on diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/gb2312prober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/gb2312prober.py deleted file mode 100644 index d423e73..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/gb2312prober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .chardistribution import GB2312DistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import GB2312_SM_MODEL - - -class GB2312Prober(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(GB2312_SM_MODEL) - self.distribution_analyzer = GB2312DistributionAnalysis() - self.reset() - - @property - def charset_name(self) -> str: - return "GB2312" - - @property - def language(self) -> str: - return "Chinese" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/hebrewprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/hebrewprober.py deleted file mode 100644 index 785d005..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/hebrewprober.py +++ /dev/null @@ -1,316 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Shy Shalom -# Portions created by the Initial Developer are Copyright (C) 2005 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Optional, Union - -from .charsetprober import CharSetProber -from .enums import ProbingState -from .sbcharsetprober import SingleByteCharSetProber - -# This prober doesn't actually recognize a language or a charset. -# It is a helper prober for the use of the Hebrew model probers - -### General ideas of the Hebrew charset recognition ### -# -# Four main charsets exist in Hebrew: -# "ISO-8859-8" - Visual Hebrew -# "windows-1255" - Logical Hebrew -# "ISO-8859-8-I" - Logical Hebrew -# "x-mac-hebrew" - ?? Logical Hebrew ?? -# -# Both "ISO" charsets use a completely identical set of code points, whereas -# "windows-1255" and "x-mac-hebrew" are two different proper supersets of -# these code points. windows-1255 defines additional characters in the range -# 0x80-0x9F as some misc punctuation marks as well as some Hebrew-specific -# diacritics and additional 'Yiddish' ligature letters in the range 0xc0-0xd6. -# x-mac-hebrew defines similar additional code points but with a different -# mapping. -# -# As far as an average Hebrew text with no diacritics is concerned, all four -# charsets are identical with respect to code points. Meaning that for the -# main Hebrew alphabet, all four map the same values to all 27 Hebrew letters -# (including final letters). -# -# The dominant difference between these charsets is their directionality. -# "Visual" directionality means that the text is ordered as if the renderer is -# not aware of a BIDI rendering algorithm. The renderer sees the text and -# draws it from left to right. The text itself when ordered naturally is read -# backwards. A buffer of Visual Hebrew generally looks like so: -# "[last word of first line spelled backwards] [whole line ordered backwards -# and spelled backwards] [first word of first line spelled backwards] -# [end of line] [last word of second line] ... etc' " -# adding punctuation marks, numbers and English text to visual text is -# naturally also "visual" and from left to right. -# -# "Logical" directionality means the text is ordered "naturally" according to -# the order it is read. It is the responsibility of the renderer to display -# the text from right to left. A BIDI algorithm is used to place general -# punctuation marks, numbers and English text in the text. -# -# Texts in x-mac-hebrew are almost impossible to find on the Internet. From -# what little evidence I could find, it seems that its general directionality -# is Logical. -# -# To sum up all of the above, the Hebrew probing mechanism knows about two -# charsets: -# Visual Hebrew - "ISO-8859-8" - backwards text - Words and sentences are -# backwards while line order is natural. For charset recognition purposes -# the line order is unimportant (In fact, for this implementation, even -# word order is unimportant). -# Logical Hebrew - "windows-1255" - normal, naturally ordered text. -# -# "ISO-8859-8-I" is a subset of windows-1255 and doesn't need to be -# specifically identified. -# "x-mac-hebrew" is also identified as windows-1255. A text in x-mac-hebrew -# that contain special punctuation marks or diacritics is displayed with -# some unconverted characters showing as question marks. This problem might -# be corrected using another model prober for x-mac-hebrew. Due to the fact -# that x-mac-hebrew texts are so rare, writing another model prober isn't -# worth the effort and performance hit. -# -#### The Prober #### -# -# The prober is divided between two SBCharSetProbers and a HebrewProber, -# all of which are managed, created, fed data, inquired and deleted by the -# SBCSGroupProber. The two SBCharSetProbers identify that the text is in -# fact some kind of Hebrew, Logical or Visual. The final decision about which -# one is it is made by the HebrewProber by combining final-letter scores -# with the scores of the two SBCharSetProbers to produce a final answer. -# -# The SBCSGroupProber is responsible for stripping the original text of HTML -# tags, English characters, numbers, low-ASCII punctuation characters, spaces -# and new lines. It reduces any sequence of such characters to a single space. -# The buffer fed to each prober in the SBCS group prober is pure text in -# high-ASCII. -# The two SBCharSetProbers (model probers) share the same language model: -# Win1255Model. -# The first SBCharSetProber uses the model normally as any other -# SBCharSetProber does, to recognize windows-1255, upon which this model was -# built. The second SBCharSetProber is told to make the pair-of-letter -# lookup in the language model backwards. This in practice exactly simulates -# a visual Hebrew model using the windows-1255 logical Hebrew model. -# -# The HebrewProber is not using any language model. All it does is look for -# final-letter evidence suggesting the text is either logical Hebrew or visual -# Hebrew. Disjointed from the model probers, the results of the HebrewProber -# alone are meaningless. HebrewProber always returns 0.00 as confidence -# since it never identifies a charset by itself. Instead, the pointer to the -# HebrewProber is passed to the model probers as a helper "Name Prober". -# When the Group prober receives a positive identification from any prober, -# it asks for the name of the charset identified. If the prober queried is a -# Hebrew model prober, the model prober forwards the call to the -# HebrewProber to make the final decision. In the HebrewProber, the -# decision is made according to the final-letters scores maintained and Both -# model probers scores. The answer is returned in the form of the name of the -# charset identified, either "windows-1255" or "ISO-8859-8". - - -class HebrewProber(CharSetProber): - SPACE = 0x20 - # windows-1255 / ISO-8859-8 code points of interest - FINAL_KAF = 0xEA - NORMAL_KAF = 0xEB - FINAL_MEM = 0xED - NORMAL_MEM = 0xEE - FINAL_NUN = 0xEF - NORMAL_NUN = 0xF0 - FINAL_PE = 0xF3 - NORMAL_PE = 0xF4 - FINAL_TSADI = 0xF5 - NORMAL_TSADI = 0xF6 - - # Minimum Visual vs Logical final letter score difference. - # If the difference is below this, don't rely solely on the final letter score - # distance. - MIN_FINAL_CHAR_DISTANCE = 5 - - # Minimum Visual vs Logical model score difference. - # If the difference is below this, don't rely at all on the model score - # distance. - MIN_MODEL_DISTANCE = 0.01 - - VISUAL_HEBREW_NAME = "ISO-8859-8" - LOGICAL_HEBREW_NAME = "windows-1255" - - def __init__(self) -> None: - super().__init__() - self._final_char_logical_score = 0 - self._final_char_visual_score = 0 - self._prev = self.SPACE - self._before_prev = self.SPACE - self._logical_prober: Optional[SingleByteCharSetProber] = None - self._visual_prober: Optional[SingleByteCharSetProber] = None - self.reset() - - def reset(self) -> None: - self._final_char_logical_score = 0 - self._final_char_visual_score = 0 - # The two last characters seen in the previous buffer, - # mPrev and mBeforePrev are initialized to space in order to simulate - # a word delimiter at the beginning of the data - self._prev = self.SPACE - self._before_prev = self.SPACE - # These probers are owned by the group prober. - - def set_model_probers( - self, - logical_prober: SingleByteCharSetProber, - visual_prober: SingleByteCharSetProber, - ) -> None: - self._logical_prober = logical_prober - self._visual_prober = visual_prober - - def is_final(self, c: int) -> bool: - return c in [ - self.FINAL_KAF, - self.FINAL_MEM, - self.FINAL_NUN, - self.FINAL_PE, - self.FINAL_TSADI, - ] - - def is_non_final(self, c: int) -> bool: - # The normal Tsadi is not a good Non-Final letter due to words like - # 'lechotet' (to chat) containing an apostrophe after the tsadi. This - # apostrophe is converted to a space in FilterWithoutEnglishLetters - # causing the Non-Final tsadi to appear at an end of a word even - # though this is not the case in the original text. - # The letters Pe and Kaf rarely display a related behavior of not being - # a good Non-Final letter. Words like 'Pop', 'Winamp' and 'Mubarak' - # for example legally end with a Non-Final Pe or Kaf. However, the - # benefit of these letters as Non-Final letters outweighs the damage - # since these words are quite rare. - return c in [self.NORMAL_KAF, self.NORMAL_MEM, self.NORMAL_NUN, self.NORMAL_PE] - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - # Final letter analysis for logical-visual decision. - # Look for evidence that the received buffer is either logical Hebrew - # or visual Hebrew. - # The following cases are checked: - # 1) A word longer than 1 letter, ending with a final letter. This is - # an indication that the text is laid out "naturally" since the - # final letter really appears at the end. +1 for logical score. - # 2) A word longer than 1 letter, ending with a Non-Final letter. In - # normal Hebrew, words ending with Kaf, Mem, Nun, Pe or Tsadi, - # should not end with the Non-Final form of that letter. Exceptions - # to this rule are mentioned above in isNonFinal(). This is an - # indication that the text is laid out backwards. +1 for visual - # score - # 3) A word longer than 1 letter, starting with a final letter. Final - # letters should not appear at the beginning of a word. This is an - # indication that the text is laid out backwards. +1 for visual - # score. - # - # The visual score and logical score are accumulated throughout the - # text and are finally checked against each other in GetCharSetName(). - # No checking for final letters in the middle of words is done since - # that case is not an indication for either Logical or Visual text. - # - # We automatically filter out all 7-bit characters (replace them with - # spaces) so the word boundary detection works properly. [MAP] - - if self.state == ProbingState.NOT_ME: - # Both model probers say it's not them. No reason to continue. - return ProbingState.NOT_ME - - byte_str = self.filter_high_byte_only(byte_str) - - for cur in byte_str: - if cur == self.SPACE: - # We stand on a space - a word just ended - if self._before_prev != self.SPACE: - # next-to-last char was not a space so self._prev is not a - # 1 letter word - if self.is_final(self._prev): - # case (1) [-2:not space][-1:final letter][cur:space] - self._final_char_logical_score += 1 - elif self.is_non_final(self._prev): - # case (2) [-2:not space][-1:Non-Final letter][ - # cur:space] - self._final_char_visual_score += 1 - else: - # Not standing on a space - if ( - (self._before_prev == self.SPACE) - and (self.is_final(self._prev)) - and (cur != self.SPACE) - ): - # case (3) [-2:space][-1:final letter][cur:not space] - self._final_char_visual_score += 1 - self._before_prev = self._prev - self._prev = cur - - # Forever detecting, till the end or until both model probers return - # ProbingState.NOT_ME (handled above) - return ProbingState.DETECTING - - @property - def charset_name(self) -> str: - assert self._logical_prober is not None - assert self._visual_prober is not None - - # Make the decision: is it Logical or Visual? - # If the final letter score distance is dominant enough, rely on it. - finalsub = self._final_char_logical_score - self._final_char_visual_score - if finalsub >= self.MIN_FINAL_CHAR_DISTANCE: - return self.LOGICAL_HEBREW_NAME - if finalsub <= -self.MIN_FINAL_CHAR_DISTANCE: - return self.VISUAL_HEBREW_NAME - - # It's not dominant enough, try to rely on the model scores instead. - modelsub = ( - self._logical_prober.get_confidence() - self._visual_prober.get_confidence() - ) - if modelsub > self.MIN_MODEL_DISTANCE: - return self.LOGICAL_HEBREW_NAME - if modelsub < -self.MIN_MODEL_DISTANCE: - return self.VISUAL_HEBREW_NAME - - # Still no good, back to final letter distance, maybe it'll save the - # day. - if finalsub < 0.0: - return self.VISUAL_HEBREW_NAME - - # (finalsub > 0 - Logical) or (don't know what to do) default to - # Logical. - return self.LOGICAL_HEBREW_NAME - - @property - def language(self) -> str: - return "Hebrew" - - @property - def state(self) -> ProbingState: - assert self._logical_prober is not None - assert self._visual_prober is not None - - # Remain active as long as any of the model probers are active. - if (self._logical_prober.state == ProbingState.NOT_ME) and ( - self._visual_prober.state == ProbingState.NOT_ME - ): - return ProbingState.NOT_ME - return ProbingState.DETECTING diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/jisfreq.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/jisfreq.py deleted file mode 100644 index 3293576..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/jisfreq.py +++ /dev/null @@ -1,325 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# Sampling from about 20M text materials include literature and computer technology -# -# Japanese frequency table, applied to both S-JIS and EUC-JP -# They are sorted in order. - -# 128 --> 0.77094 -# 256 --> 0.85710 -# 512 --> 0.92635 -# 1024 --> 0.97130 -# 2048 --> 0.99431 -# -# Ideal Distribution Ratio = 0.92635 / (1-0.92635) = 12.58 -# Random Distribution Ration = 512 / (2965+62+83+86-512) = 0.191 -# -# Typical Distribution Ratio, 25% of IDR - -JIS_TYPICAL_DISTRIBUTION_RATIO = 3.0 - -# Char to FreqOrder table , -JIS_TABLE_SIZE = 4368 - -# fmt: off -JIS_CHAR_TO_FREQ_ORDER = ( - 40, 1, 6, 182, 152, 180, 295,2127, 285, 381,3295,4304,3068,4606,3165,3510, # 16 -3511,1822,2785,4607,1193,2226,5070,4608, 171,2996,1247, 18, 179,5071, 856,1661, # 32 -1262,5072, 619, 127,3431,3512,3230,1899,1700, 232, 228,1294,1298, 284, 283,2041, # 48 -2042,1061,1062, 48, 49, 44, 45, 433, 434,1040,1041, 996, 787,2997,1255,4305, # 64 -2108,4609,1684,1648,5073,5074,5075,5076,5077,5078,3687,5079,4610,5080,3927,3928, # 80 -5081,3296,3432, 290,2285,1471,2187,5082,2580,2825,1303,2140,1739,1445,2691,3375, # 96 -1691,3297,4306,4307,4611, 452,3376,1182,2713,3688,3069,4308,5083,5084,5085,5086, # 112 -5087,5088,5089,5090,5091,5092,5093,5094,5095,5096,5097,5098,5099,5100,5101,5102, # 128 -5103,5104,5105,5106,5107,5108,5109,5110,5111,5112,4097,5113,5114,5115,5116,5117, # 144 -5118,5119,5120,5121,5122,5123,5124,5125,5126,5127,5128,5129,5130,5131,5132,5133, # 160 -5134,5135,5136,5137,5138,5139,5140,5141,5142,5143,5144,5145,5146,5147,5148,5149, # 176 -5150,5151,5152,4612,5153,5154,5155,5156,5157,5158,5159,5160,5161,5162,5163,5164, # 192 -5165,5166,5167,5168,5169,5170,5171,5172,5173,5174,5175,1472, 598, 618, 820,1205, # 208 -1309,1412,1858,1307,1692,5176,5177,5178,5179,5180,5181,5182,1142,1452,1234,1172, # 224 -1875,2043,2149,1793,1382,2973, 925,2404,1067,1241, 960,1377,2935,1491, 919,1217, # 240 -1865,2030,1406,1499,2749,4098,5183,5184,5185,5186,5187,5188,2561,4099,3117,1804, # 256 -2049,3689,4309,3513,1663,5189,3166,3118,3298,1587,1561,3433,5190,3119,1625,2998, # 272 -3299,4613,1766,3690,2786,4614,5191,5192,5193,5194,2161, 26,3377, 2,3929, 20, # 288 -3691, 47,4100, 50, 17, 16, 35, 268, 27, 243, 42, 155, 24, 154, 29, 184, # 304 - 4, 91, 14, 92, 53, 396, 33, 289, 9, 37, 64, 620, 21, 39, 321, 5, # 320 - 12, 11, 52, 13, 3, 208, 138, 0, 7, 60, 526, 141, 151,1069, 181, 275, # 336 -1591, 83, 132,1475, 126, 331, 829, 15, 69, 160, 59, 22, 157, 55,1079, 312, # 352 - 109, 38, 23, 25, 10, 19, 79,5195, 61, 382,1124, 8, 30,5196,5197,5198, # 368 -5199,5200,5201,5202,5203,5204,5205,5206, 89, 62, 74, 34,2416, 112, 139, 196, # 384 - 271, 149, 84, 607, 131, 765, 46, 88, 153, 683, 76, 874, 101, 258, 57, 80, # 400 - 32, 364, 121,1508, 169,1547, 68, 235, 145,2999, 41, 360,3027, 70, 63, 31, # 416 - 43, 259, 262,1383, 99, 533, 194, 66, 93, 846, 217, 192, 56, 106, 58, 565, # 432 - 280, 272, 311, 256, 146, 82, 308, 71, 100, 128, 214, 655, 110, 261, 104,1140, # 448 - 54, 51, 36, 87, 67,3070, 185,2618,2936,2020, 28,1066,2390,2059,5207,5208, # 464 -5209,5210,5211,5212,5213,5214,5215,5216,4615,5217,5218,5219,5220,5221,5222,5223, # 480 -5224,5225,5226,5227,5228,5229,5230,5231,5232,5233,5234,5235,5236,3514,5237,5238, # 496 -5239,5240,5241,5242,5243,5244,2297,2031,4616,4310,3692,5245,3071,5246,3598,5247, # 512 -4617,3231,3515,5248,4101,4311,4618,3808,4312,4102,5249,4103,4104,3599,5250,5251, # 528 -5252,5253,5254,5255,5256,5257,5258,5259,5260,5261,5262,5263,5264,5265,5266,5267, # 544 -5268,5269,5270,5271,5272,5273,5274,5275,5276,5277,5278,5279,5280,5281,5282,5283, # 560 -5284,5285,5286,5287,5288,5289,5290,5291,5292,5293,5294,5295,5296,5297,5298,5299, # 576 -5300,5301,5302,5303,5304,5305,5306,5307,5308,5309,5310,5311,5312,5313,5314,5315, # 592 -5316,5317,5318,5319,5320,5321,5322,5323,5324,5325,5326,5327,5328,5329,5330,5331, # 608 -5332,5333,5334,5335,5336,5337,5338,5339,5340,5341,5342,5343,5344,5345,5346,5347, # 624 -5348,5349,5350,5351,5352,5353,5354,5355,5356,5357,5358,5359,5360,5361,5362,5363, # 640 -5364,5365,5366,5367,5368,5369,5370,5371,5372,5373,5374,5375,5376,5377,5378,5379, # 656 -5380,5381, 363, 642,2787,2878,2788,2789,2316,3232,2317,3434,2011, 165,1942,3930, # 672 -3931,3932,3933,5382,4619,5383,4620,5384,5385,5386,5387,5388,5389,5390,5391,5392, # 688 -5393,5394,5395,5396,5397,5398,5399,5400,5401,5402,5403,5404,5405,5406,5407,5408, # 704 -5409,5410,5411,5412,5413,5414,5415,5416,5417,5418,5419,5420,5421,5422,5423,5424, # 720 -5425,5426,5427,5428,5429,5430,5431,5432,5433,5434,5435,5436,5437,5438,5439,5440, # 736 -5441,5442,5443,5444,5445,5446,5447,5448,5449,5450,5451,5452,5453,5454,5455,5456, # 752 -5457,5458,5459,5460,5461,5462,5463,5464,5465,5466,5467,5468,5469,5470,5471,5472, # 768 -5473,5474,5475,5476,5477,5478,5479,5480,5481,5482,5483,5484,5485,5486,5487,5488, # 784 -5489,5490,5491,5492,5493,5494,5495,5496,5497,5498,5499,5500,5501,5502,5503,5504, # 800 -5505,5506,5507,5508,5509,5510,5511,5512,5513,5514,5515,5516,5517,5518,5519,5520, # 816 -5521,5522,5523,5524,5525,5526,5527,5528,5529,5530,5531,5532,5533,5534,5535,5536, # 832 -5537,5538,5539,5540,5541,5542,5543,5544,5545,5546,5547,5548,5549,5550,5551,5552, # 848 -5553,5554,5555,5556,5557,5558,5559,5560,5561,5562,5563,5564,5565,5566,5567,5568, # 864 -5569,5570,5571,5572,5573,5574,5575,5576,5577,5578,5579,5580,5581,5582,5583,5584, # 880 -5585,5586,5587,5588,5589,5590,5591,5592,5593,5594,5595,5596,5597,5598,5599,5600, # 896 -5601,5602,5603,5604,5605,5606,5607,5608,5609,5610,5611,5612,5613,5614,5615,5616, # 912 -5617,5618,5619,5620,5621,5622,5623,5624,5625,5626,5627,5628,5629,5630,5631,5632, # 928 -5633,5634,5635,5636,5637,5638,5639,5640,5641,5642,5643,5644,5645,5646,5647,5648, # 944 -5649,5650,5651,5652,5653,5654,5655,5656,5657,5658,5659,5660,5661,5662,5663,5664, # 960 -5665,5666,5667,5668,5669,5670,5671,5672,5673,5674,5675,5676,5677,5678,5679,5680, # 976 -5681,5682,5683,5684,5685,5686,5687,5688,5689,5690,5691,5692,5693,5694,5695,5696, # 992 -5697,5698,5699,5700,5701,5702,5703,5704,5705,5706,5707,5708,5709,5710,5711,5712, # 1008 -5713,5714,5715,5716,5717,5718,5719,5720,5721,5722,5723,5724,5725,5726,5727,5728, # 1024 -5729,5730,5731,5732,5733,5734,5735,5736,5737,5738,5739,5740,5741,5742,5743,5744, # 1040 -5745,5746,5747,5748,5749,5750,5751,5752,5753,5754,5755,5756,5757,5758,5759,5760, # 1056 -5761,5762,5763,5764,5765,5766,5767,5768,5769,5770,5771,5772,5773,5774,5775,5776, # 1072 -5777,5778,5779,5780,5781,5782,5783,5784,5785,5786,5787,5788,5789,5790,5791,5792, # 1088 -5793,5794,5795,5796,5797,5798,5799,5800,5801,5802,5803,5804,5805,5806,5807,5808, # 1104 -5809,5810,5811,5812,5813,5814,5815,5816,5817,5818,5819,5820,5821,5822,5823,5824, # 1120 -5825,5826,5827,5828,5829,5830,5831,5832,5833,5834,5835,5836,5837,5838,5839,5840, # 1136 -5841,5842,5843,5844,5845,5846,5847,5848,5849,5850,5851,5852,5853,5854,5855,5856, # 1152 -5857,5858,5859,5860,5861,5862,5863,5864,5865,5866,5867,5868,5869,5870,5871,5872, # 1168 -5873,5874,5875,5876,5877,5878,5879,5880,5881,5882,5883,5884,5885,5886,5887,5888, # 1184 -5889,5890,5891,5892,5893,5894,5895,5896,5897,5898,5899,5900,5901,5902,5903,5904, # 1200 -5905,5906,5907,5908,5909,5910,5911,5912,5913,5914,5915,5916,5917,5918,5919,5920, # 1216 -5921,5922,5923,5924,5925,5926,5927,5928,5929,5930,5931,5932,5933,5934,5935,5936, # 1232 -5937,5938,5939,5940,5941,5942,5943,5944,5945,5946,5947,5948,5949,5950,5951,5952, # 1248 -5953,5954,5955,5956,5957,5958,5959,5960,5961,5962,5963,5964,5965,5966,5967,5968, # 1264 -5969,5970,5971,5972,5973,5974,5975,5976,5977,5978,5979,5980,5981,5982,5983,5984, # 1280 -5985,5986,5987,5988,5989,5990,5991,5992,5993,5994,5995,5996,5997,5998,5999,6000, # 1296 -6001,6002,6003,6004,6005,6006,6007,6008,6009,6010,6011,6012,6013,6014,6015,6016, # 1312 -6017,6018,6019,6020,6021,6022,6023,6024,6025,6026,6027,6028,6029,6030,6031,6032, # 1328 -6033,6034,6035,6036,6037,6038,6039,6040,6041,6042,6043,6044,6045,6046,6047,6048, # 1344 -6049,6050,6051,6052,6053,6054,6055,6056,6057,6058,6059,6060,6061,6062,6063,6064, # 1360 -6065,6066,6067,6068,6069,6070,6071,6072,6073,6074,6075,6076,6077,6078,6079,6080, # 1376 -6081,6082,6083,6084,6085,6086,6087,6088,6089,6090,6091,6092,6093,6094,6095,6096, # 1392 -6097,6098,6099,6100,6101,6102,6103,6104,6105,6106,6107,6108,6109,6110,6111,6112, # 1408 -6113,6114,2044,2060,4621, 997,1235, 473,1186,4622, 920,3378,6115,6116, 379,1108, # 1424 -4313,2657,2735,3934,6117,3809, 636,3233, 573,1026,3693,3435,2974,3300,2298,4105, # 1440 - 854,2937,2463, 393,2581,2417, 539, 752,1280,2750,2480, 140,1161, 440, 708,1569, # 1456 - 665,2497,1746,1291,1523,3000, 164,1603, 847,1331, 537,1997, 486, 508,1693,2418, # 1472 -1970,2227, 878,1220, 299,1030, 969, 652,2751, 624,1137,3301,2619, 65,3302,2045, # 1488 -1761,1859,3120,1930,3694,3516, 663,1767, 852, 835,3695, 269, 767,2826,2339,1305, # 1504 - 896,1150, 770,1616,6118, 506,1502,2075,1012,2519, 775,2520,2975,2340,2938,4314, # 1520 -3028,2086,1224,1943,2286,6119,3072,4315,2240,1273,1987,3935,1557, 175, 597, 985, # 1536 -3517,2419,2521,1416,3029, 585, 938,1931,1007,1052,1932,1685,6120,3379,4316,4623, # 1552 - 804, 599,3121,1333,2128,2539,1159,1554,2032,3810, 687,2033,2904, 952, 675,1467, # 1568 -3436,6121,2241,1096,1786,2440,1543,1924, 980,1813,2228, 781,2692,1879, 728,1918, # 1584 -3696,4624, 548,1950,4625,1809,1088,1356,3303,2522,1944, 502, 972, 373, 513,2827, # 1600 - 586,2377,2391,1003,1976,1631,6122,2464,1084, 648,1776,4626,2141, 324, 962,2012, # 1616 -2177,2076,1384, 742,2178,1448,1173,1810, 222, 102, 301, 445, 125,2420, 662,2498, # 1632 - 277, 200,1476,1165,1068, 224,2562,1378,1446, 450,1880, 659, 791, 582,4627,2939, # 1648 -3936,1516,1274, 555,2099,3697,1020,1389,1526,3380,1762,1723,1787,2229, 412,2114, # 1664 -1900,2392,3518, 512,2597, 427,1925,2341,3122,1653,1686,2465,2499, 697, 330, 273, # 1680 - 380,2162, 951, 832, 780, 991,1301,3073, 965,2270,3519, 668,2523,2636,1286, 535, # 1696 -1407, 518, 671, 957,2658,2378, 267, 611,2197,3030,6123, 248,2299, 967,1799,2356, # 1712 - 850,1418,3437,1876,1256,1480,2828,1718,6124,6125,1755,1664,2405,6126,4628,2879, # 1728 -2829, 499,2179, 676,4629, 557,2329,2214,2090, 325,3234, 464, 811,3001, 992,2342, # 1744 -2481,1232,1469, 303,2242, 466,1070,2163, 603,1777,2091,4630,2752,4631,2714, 322, # 1760 -2659,1964,1768, 481,2188,1463,2330,2857,3600,2092,3031,2421,4632,2318,2070,1849, # 1776 -2598,4633,1302,2254,1668,1701,2422,3811,2905,3032,3123,2046,4106,1763,1694,4634, # 1792 -1604, 943,1724,1454, 917, 868,2215,1169,2940, 552,1145,1800,1228,1823,1955, 316, # 1808 -1080,2510, 361,1807,2830,4107,2660,3381,1346,1423,1134,4108,6127, 541,1263,1229, # 1824 -1148,2540, 545, 465,1833,2880,3438,1901,3074,2482, 816,3937, 713,1788,2500, 122, # 1840 -1575, 195,1451,2501,1111,6128, 859, 374,1225,2243,2483,4317, 390,1033,3439,3075, # 1856 -2524,1687, 266, 793,1440,2599, 946, 779, 802, 507, 897,1081, 528,2189,1292, 711, # 1872 -1866,1725,1167,1640, 753, 398,2661,1053, 246, 348,4318, 137,1024,3440,1600,2077, # 1888 -2129, 825,4319, 698, 238, 521, 187,2300,1157,2423,1641,1605,1464,1610,1097,2541, # 1904 -1260,1436, 759,2255,1814,2150, 705,3235, 409,2563,3304, 561,3033,2005,2564, 726, # 1920 -1956,2343,3698,4109, 949,3812,3813,3520,1669, 653,1379,2525, 881,2198, 632,2256, # 1936 -1027, 778,1074, 733,1957, 514,1481,2466, 554,2180, 702,3938,1606,1017,1398,6129, # 1952 -1380,3521, 921, 993,1313, 594, 449,1489,1617,1166, 768,1426,1360, 495,1794,3601, # 1968 -1177,3602,1170,4320,2344, 476, 425,3167,4635,3168,1424, 401,2662,1171,3382,1998, # 1984 -1089,4110, 477,3169, 474,6130,1909, 596,2831,1842, 494, 693,1051,1028,1207,3076, # 2000 - 606,2115, 727,2790,1473,1115, 743,3522, 630, 805,1532,4321,2021, 366,1057, 838, # 2016 - 684,1114,2142,4322,2050,1492,1892,1808,2271,3814,2424,1971,1447,1373,3305,1090, # 2032 -1536,3939,3523,3306,1455,2199, 336, 369,2331,1035, 584,2393, 902, 718,2600,6131, # 2048 -2753, 463,2151,1149,1611,2467, 715,1308,3124,1268, 343,1413,3236,1517,1347,2663, # 2064 -2093,3940,2022,1131,1553,2100,2941,1427,3441,2942,1323,2484,6132,1980, 872,2368, # 2080 -2441,2943, 320,2369,2116,1082, 679,1933,3941,2791,3815, 625,1143,2023, 422,2200, # 2096 -3816,6133, 730,1695, 356,2257,1626,2301,2858,2637,1627,1778, 937, 883,2906,2693, # 2112 -3002,1769,1086, 400,1063,1325,3307,2792,4111,3077, 456,2345,1046, 747,6134,1524, # 2128 - 884,1094,3383,1474,2164,1059, 974,1688,2181,2258,1047, 345,1665,1187, 358, 875, # 2144 -3170, 305, 660,3524,2190,1334,1135,3171,1540,1649,2542,1527, 927, 968,2793, 885, # 2160 -1972,1850, 482, 500,2638,1218,1109,1085,2543,1654,2034, 876, 78,2287,1482,1277, # 2176 - 861,1675,1083,1779, 724,2754, 454, 397,1132,1612,2332, 893, 672,1237, 257,2259, # 2192 -2370, 135,3384, 337,2244, 547, 352, 340, 709,2485,1400, 788,1138,2511, 540, 772, # 2208 -1682,2260,2272,2544,2013,1843,1902,4636,1999,1562,2288,4637,2201,1403,1533, 407, # 2224 - 576,3308,1254,2071, 978,3385, 170, 136,1201,3125,2664,3172,2394, 213, 912, 873, # 2240 -3603,1713,2202, 699,3604,3699, 813,3442, 493, 531,1054, 468,2907,1483, 304, 281, # 2256 -4112,1726,1252,2094, 339,2319,2130,2639, 756,1563,2944, 748, 571,2976,1588,2425, # 2272 -2715,1851,1460,2426,1528,1392,1973,3237, 288,3309, 685,3386, 296, 892,2716,2216, # 2288 -1570,2245, 722,1747,2217, 905,3238,1103,6135,1893,1441,1965, 251,1805,2371,3700, # 2304 -2601,1919,1078, 75,2182,1509,1592,1270,2640,4638,2152,6136,3310,3817, 524, 706, # 2320 -1075, 292,3818,1756,2602, 317, 98,3173,3605,3525,1844,2218,3819,2502, 814, 567, # 2336 - 385,2908,1534,6137, 534,1642,3239, 797,6138,1670,1529, 953,4323, 188,1071, 538, # 2352 - 178, 729,3240,2109,1226,1374,2000,2357,2977, 731,2468,1116,2014,2051,6139,1261, # 2368 -1593, 803,2859,2736,3443, 556, 682, 823,1541,6140,1369,2289,1706,2794, 845, 462, # 2384 -2603,2665,1361, 387, 162,2358,1740, 739,1770,1720,1304,1401,3241,1049, 627,1571, # 2400 -2427,3526,1877,3942,1852,1500, 431,1910,1503, 677, 297,2795, 286,1433,1038,1198, # 2416 -2290,1133,1596,4113,4639,2469,1510,1484,3943,6141,2442, 108, 712,4640,2372, 866, # 2432 -3701,2755,3242,1348, 834,1945,1408,3527,2395,3243,1811, 824, 994,1179,2110,1548, # 2448 -1453, 790,3003, 690,4324,4325,2832,2909,3820,1860,3821, 225,1748, 310, 346,1780, # 2464 -2470, 821,1993,2717,2796, 828, 877,3528,2860,2471,1702,2165,2910,2486,1789, 453, # 2480 - 359,2291,1676, 73,1164,1461,1127,3311, 421, 604, 314,1037, 589, 116,2487, 737, # 2496 - 837,1180, 111, 244, 735,6142,2261,1861,1362, 986, 523, 418, 581,2666,3822, 103, # 2512 - 855, 503,1414,1867,2488,1091, 657,1597, 979, 605,1316,4641,1021,2443,2078,2001, # 2528 -1209, 96, 587,2166,1032, 260,1072,2153, 173, 94, 226,3244, 819,2006,4642,4114, # 2544 -2203, 231,1744, 782, 97,2667, 786,3387, 887, 391, 442,2219,4326,1425,6143,2694, # 2560 - 633,1544,1202, 483,2015, 592,2052,1958,2472,1655, 419, 129,4327,3444,3312,1714, # 2576 -1257,3078,4328,1518,1098, 865,1310,1019,1885,1512,1734, 469,2444, 148, 773, 436, # 2592 -1815,1868,1128,1055,4329,1245,2756,3445,2154,1934,1039,4643, 579,1238, 932,2320, # 2608 - 353, 205, 801, 115,2428, 944,2321,1881, 399,2565,1211, 678, 766,3944, 335,2101, # 2624 -1459,1781,1402,3945,2737,2131,1010, 844, 981,1326,1013, 550,1816,1545,2620,1335, # 2640 -1008, 371,2881, 936,1419,1613,3529,1456,1395,2273,1834,2604,1317,2738,2503, 416, # 2656 -1643,4330, 806,1126, 229, 591,3946,1314,1981,1576,1837,1666, 347,1790, 977,3313, # 2672 - 764,2861,1853, 688,2429,1920,1462, 77, 595, 415,2002,3034, 798,1192,4115,6144, # 2688 -2978,4331,3035,2695,2582,2072,2566, 430,2430,1727, 842,1396,3947,3702, 613, 377, # 2704 - 278, 236,1417,3388,3314,3174, 757,1869, 107,3530,6145,1194, 623,2262, 207,1253, # 2720 -2167,3446,3948, 492,1117,1935, 536,1838,2757,1246,4332, 696,2095,2406,1393,1572, # 2736 -3175,1782, 583, 190, 253,1390,2230, 830,3126,3389, 934,3245,1703,1749,2979,1870, # 2752 -2545,1656,2204, 869,2346,4116,3176,1817, 496,1764,4644, 942,1504, 404,1903,1122, # 2768 -1580,3606,2945,1022, 515, 372,1735, 955,2431,3036,6146,2797,1110,2302,2798, 617, # 2784 -6147, 441, 762,1771,3447,3607,3608,1904, 840,3037, 86, 939,1385, 572,1370,2445, # 2800 -1336, 114,3703, 898, 294, 203,3315, 703,1583,2274, 429, 961,4333,1854,1951,3390, # 2816 -2373,3704,4334,1318,1381, 966,1911,2322,1006,1155, 309, 989, 458,2718,1795,1372, # 2832 -1203, 252,1689,1363,3177, 517,1936, 168,1490, 562, 193,3823,1042,4117,1835, 551, # 2848 - 470,4645, 395, 489,3448,1871,1465,2583,2641, 417,1493, 279,1295, 511,1236,1119, # 2864 - 72,1231,1982,1812,3004, 871,1564, 984,3449,1667,2696,2096,4646,2347,2833,1673, # 2880 -3609, 695,3246,2668, 807,1183,4647, 890, 388,2333,1801,1457,2911,1765,1477,1031, # 2896 -3316,3317,1278,3391,2799,2292,2526, 163,3450,4335,2669,1404,1802,6148,2323,2407, # 2912 -1584,1728,1494,1824,1269, 298, 909,3318,1034,1632, 375, 776,1683,2061, 291, 210, # 2928 -1123, 809,1249,1002,2642,3038, 206,1011,2132, 144, 975, 882,1565, 342, 667, 754, # 2944 -1442,2143,1299,2303,2062, 447, 626,2205,1221,2739,2912,1144,1214,2206,2584, 760, # 2960 -1715, 614, 950,1281,2670,2621, 810, 577,1287,2546,4648, 242,2168, 250,2643, 691, # 2976 - 123,2644, 647, 313,1029, 689,1357,2946,1650, 216, 771,1339,1306, 808,2063, 549, # 2992 - 913,1371,2913,2914,6149,1466,1092,1174,1196,1311,2605,2396,1783,1796,3079, 406, # 3008 -2671,2117,3949,4649, 487,1825,2220,6150,2915, 448,2348,1073,6151,2397,1707, 130, # 3024 - 900,1598, 329, 176,1959,2527,1620,6152,2275,4336,3319,1983,2191,3705,3610,2155, # 3040 -3706,1912,1513,1614,6153,1988, 646, 392,2304,1589,3320,3039,1826,1239,1352,1340, # 3056 -2916, 505,2567,1709,1437,2408,2547, 906,6154,2672, 384,1458,1594,1100,1329, 710, # 3072 - 423,3531,2064,2231,2622,1989,2673,1087,1882, 333, 841,3005,1296,2882,2379, 580, # 3088 -1937,1827,1293,2585, 601, 574, 249,1772,4118,2079,1120, 645, 901,1176,1690, 795, # 3104 -2207, 478,1434, 516,1190,1530, 761,2080, 930,1264, 355, 435,1552, 644,1791, 987, # 3120 - 220,1364,1163,1121,1538, 306,2169,1327,1222, 546,2645, 218, 241, 610,1704,3321, # 3136 -1984,1839,1966,2528, 451,6155,2586,3707,2568, 907,3178, 254,2947, 186,1845,4650, # 3152 - 745, 432,1757, 428,1633, 888,2246,2221,2489,3611,2118,1258,1265, 956,3127,1784, # 3168 -4337,2490, 319, 510, 119, 457,3612, 274,2035,2007,4651,1409,3128, 970,2758, 590, # 3184 -2800, 661,2247,4652,2008,3950,1420,1549,3080,3322,3951,1651,1375,2111, 485,2491, # 3200 -1429,1156,6156,2548,2183,1495, 831,1840,2529,2446, 501,1657, 307,1894,3247,1341, # 3216 - 666, 899,2156,1539,2549,1559, 886, 349,2208,3081,2305,1736,3824,2170,2759,1014, # 3232 -1913,1386, 542,1397,2948, 490, 368, 716, 362, 159, 282,2569,1129,1658,1288,1750, # 3248 -2674, 276, 649,2016, 751,1496, 658,1818,1284,1862,2209,2087,2512,3451, 622,2834, # 3264 - 376, 117,1060,2053,1208,1721,1101,1443, 247,1250,3179,1792,3952,2760,2398,3953, # 3280 -6157,2144,3708, 446,2432,1151,2570,3452,2447,2761,2835,1210,2448,3082, 424,2222, # 3296 -1251,2449,2119,2836, 504,1581,4338, 602, 817, 857,3825,2349,2306, 357,3826,1470, # 3312 -1883,2883, 255, 958, 929,2917,3248, 302,4653,1050,1271,1751,2307,1952,1430,2697, # 3328 -2719,2359, 354,3180, 777, 158,2036,4339,1659,4340,4654,2308,2949,2248,1146,2232, # 3344 -3532,2720,1696,2623,3827,6158,3129,1550,2698,1485,1297,1428, 637, 931,2721,2145, # 3360 - 914,2550,2587, 81,2450, 612, 827,2646,1242,4655,1118,2884, 472,1855,3181,3533, # 3376 -3534, 569,1353,2699,1244,1758,2588,4119,2009,2762,2171,3709,1312,1531,6159,1152, # 3392 -1938, 134,1830, 471,3710,2276,1112,1535,3323,3453,3535, 982,1337,2950, 488, 826, # 3408 - 674,1058,1628,4120,2017, 522,2399, 211, 568,1367,3454, 350, 293,1872,1139,3249, # 3424 -1399,1946,3006,1300,2360,3324, 588, 736,6160,2606, 744, 669,3536,3828,6161,1358, # 3440 - 199, 723, 848, 933, 851,1939,1505,1514,1338,1618,1831,4656,1634,3613, 443,2740, # 3456 -3829, 717,1947, 491,1914,6162,2551,1542,4121,1025,6163,1099,1223, 198,3040,2722, # 3472 - 370, 410,1905,2589, 998,1248,3182,2380, 519,1449,4122,1710, 947, 928,1153,4341, # 3488 -2277, 344,2624,1511, 615, 105, 161,1212,1076,1960,3130,2054,1926,1175,1906,2473, # 3504 - 414,1873,2801,6164,2309, 315,1319,3325, 318,2018,2146,2157, 963, 631, 223,4342, # 3520 -4343,2675, 479,3711,1197,2625,3712,2676,2361,6165,4344,4123,6166,2451,3183,1886, # 3536 -2184,1674,1330,1711,1635,1506, 799, 219,3250,3083,3954,1677,3713,3326,2081,3614, # 3552 -1652,2073,4657,1147,3041,1752, 643,1961, 147,1974,3955,6167,1716,2037, 918,3007, # 3568 -1994, 120,1537, 118, 609,3184,4345, 740,3455,1219, 332,1615,3830,6168,1621,2980, # 3584 -1582, 783, 212, 553,2350,3714,1349,2433,2082,4124, 889,6169,2310,1275,1410, 973, # 3600 - 166,1320,3456,1797,1215,3185,2885,1846,2590,2763,4658, 629, 822,3008, 763, 940, # 3616 -1990,2862, 439,2409,1566,1240,1622, 926,1282,1907,2764, 654,2210,1607, 327,1130, # 3632 -3956,1678,1623,6170,2434,2192, 686, 608,3831,3715, 903,3957,3042,6171,2741,1522, # 3648 -1915,1105,1555,2552,1359, 323,3251,4346,3457, 738,1354,2553,2311,2334,1828,2003, # 3664 -3832,1753,2351,1227,6172,1887,4125,1478,6173,2410,1874,1712,1847, 520,1204,2607, # 3680 - 264,4659, 836,2677,2102, 600,4660,3833,2278,3084,6174,4347,3615,1342, 640, 532, # 3696 - 543,2608,1888,2400,2591,1009,4348,1497, 341,1737,3616,2723,1394, 529,3252,1321, # 3712 - 983,4661,1515,2120, 971,2592, 924, 287,1662,3186,4349,2700,4350,1519, 908,1948, # 3728 -2452, 156, 796,1629,1486,2223,2055, 694,4126,1259,1036,3392,1213,2249,2742,1889, # 3744 -1230,3958,1015, 910, 408, 559,3617,4662, 746, 725, 935,4663,3959,3009,1289, 563, # 3760 - 867,4664,3960,1567,2981,2038,2626, 988,2263,2381,4351, 143,2374, 704,1895,6175, # 3776 -1188,3716,2088, 673,3085,2362,4352, 484,1608,1921,2765,2918, 215, 904,3618,3537, # 3792 - 894, 509, 976,3043,2701,3961,4353,2837,2982, 498,6176,6177,1102,3538,1332,3393, # 3808 -1487,1636,1637, 233, 245,3962, 383, 650, 995,3044, 460,1520,1206,2352, 749,3327, # 3824 - 530, 700, 389,1438,1560,1773,3963,2264, 719,2951,2724,3834, 870,1832,1644,1000, # 3840 - 839,2474,3717, 197,1630,3394, 365,2886,3964,1285,2133, 734, 922, 818,1106, 732, # 3856 - 480,2083,1774,3458, 923,2279,1350, 221,3086, 85,2233,2234,3835,1585,3010,2147, # 3872 -1387,1705,2382,1619,2475, 133, 239,2802,1991,1016,2084,2383, 411,2838,1113, 651, # 3888 -1985,1160,3328, 990,1863,3087,1048,1276,2647, 265,2627,1599,3253,2056, 150, 638, # 3904 -2019, 656, 853, 326,1479, 680,1439,4354,1001,1759, 413,3459,3395,2492,1431, 459, # 3920 -4355,1125,3329,2265,1953,1450,2065,2863, 849, 351,2678,3131,3254,3255,1104,1577, # 3936 - 227,1351,1645,2453,2193,1421,2887, 812,2121, 634, 95,2435, 201,2312,4665,1646, # 3952 -1671,2743,1601,2554,2702,2648,2280,1315,1366,2089,3132,1573,3718,3965,1729,1189, # 3968 - 328,2679,1077,1940,1136, 558,1283, 964,1195, 621,2074,1199,1743,3460,3619,1896, # 3984 -1916,1890,3836,2952,1154,2112,1064, 862, 378,3011,2066,2113,2803,1568,2839,6178, # 4000 -3088,2919,1941,1660,2004,1992,2194, 142, 707,1590,1708,1624,1922,1023,1836,1233, # 4016 -1004,2313, 789, 741,3620,6179,1609,2411,1200,4127,3719,3720,4666,2057,3721, 593, # 4032 -2840, 367,2920,1878,6180,3461,1521, 628,1168, 692,2211,2649, 300, 720,2067,2571, # 4048 -2953,3396, 959,2504,3966,3539,3462,1977, 701,6181, 954,1043, 800, 681, 183,3722, # 4064 -1803,1730,3540,4128,2103, 815,2314, 174, 467, 230,2454,1093,2134, 755,3541,3397, # 4080 -1141,1162,6182,1738,2039, 270,3256,2513,1005,1647,2185,3837, 858,1679,1897,1719, # 4096 -2954,2324,1806, 402, 670, 167,4129,1498,2158,2104, 750,6183, 915, 189,1680,1551, # 4112 - 455,4356,1501,2455, 405,1095,2955, 338,1586,1266,1819, 570, 641,1324, 237,1556, # 4128 -2650,1388,3723,6184,1368,2384,1343,1978,3089,2436, 879,3724, 792,1191, 758,3012, # 4144 -1411,2135,1322,4357, 240,4667,1848,3725,1574,6185, 420,3045,1546,1391, 714,4358, # 4160 -1967, 941,1864, 863, 664, 426, 560,1731,2680,1785,2864,1949,2363, 403,3330,1415, # 4176 -1279,2136,1697,2335, 204, 721,2097,3838, 90,6186,2085,2505, 191,3967, 124,2148, # 4192 -1376,1798,1178,1107,1898,1405, 860,4359,1243,1272,2375,2983,1558,2456,1638, 113, # 4208 -3621, 578,1923,2609, 880, 386,4130, 784,2186,2266,1422,2956,2172,1722, 497, 263, # 4224 -2514,1267,2412,2610, 177,2703,3542, 774,1927,1344, 616,1432,1595,1018, 172,4360, # 4240 -2325, 911,4361, 438,1468,3622, 794,3968,2024,2173,1681,1829,2957, 945, 895,3090, # 4256 - 575,2212,2476, 475,2401,2681, 785,2744,1745,2293,2555,1975,3133,2865, 394,4668, # 4272 -3839, 635,4131, 639, 202,1507,2195,2766,1345,1435,2572,3726,1908,1184,1181,2457, # 4288 -3727,3134,4362, 843,2611, 437, 916,4669, 234, 769,1884,3046,3047,3623, 833,6187, # 4304 -1639,2250,2402,1355,1185,2010,2047, 999, 525,1732,1290,1488,2612, 948,1578,3728, # 4320 -2413,2477,1216,2725,2159, 334,3840,1328,3624,2921,1525,4132, 564,1056, 891,4363, # 4336 -1444,1698,2385,2251,3729,1365,2281,2235,1717,6188, 864,3841,2515, 444, 527,2767, # 4352 -2922,3625, 544, 461,6189, 566, 209,2437,3398,2098,1065,2068,3331,3626,3257,2137, # 4368 #last 512 -) -# fmt: on diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/johabfreq.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/johabfreq.py deleted file mode 100644 index c129699..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/johabfreq.py +++ /dev/null @@ -1,2382 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -# The frequency data itself is the same as euc-kr. -# This is just a mapping table to euc-kr. - -JOHAB_TO_EUCKR_ORDER_TABLE = { - 0x8861: 0, - 0x8862: 1, - 0x8865: 2, - 0x8868: 3, - 0x8869: 4, - 0x886A: 5, - 0x886B: 6, - 0x8871: 7, - 0x8873: 8, - 0x8874: 9, - 0x8875: 10, - 0x8876: 11, - 0x8877: 12, - 0x8878: 13, - 0x8879: 14, - 0x887B: 15, - 0x887C: 16, - 0x887D: 17, - 0x8881: 18, - 0x8882: 19, - 0x8885: 20, - 0x8889: 21, - 0x8891: 22, - 0x8893: 23, - 0x8895: 24, - 0x8896: 25, - 0x8897: 26, - 0x88A1: 27, - 0x88A2: 28, - 0x88A5: 29, - 0x88A9: 30, - 0x88B5: 31, - 0x88B7: 32, - 0x88C1: 33, - 0x88C5: 34, - 0x88C9: 35, - 0x88E1: 36, - 0x88E2: 37, - 0x88E5: 38, - 0x88E8: 39, - 0x88E9: 40, - 0x88EB: 41, - 0x88F1: 42, - 0x88F3: 43, - 0x88F5: 44, - 0x88F6: 45, - 0x88F7: 46, - 0x88F8: 47, - 0x88FB: 48, - 0x88FC: 49, - 0x88FD: 50, - 0x8941: 51, - 0x8945: 52, - 0x8949: 53, - 0x8951: 54, - 0x8953: 55, - 0x8955: 56, - 0x8956: 57, - 0x8957: 58, - 0x8961: 59, - 0x8962: 60, - 0x8963: 61, - 0x8965: 62, - 0x8968: 63, - 0x8969: 64, - 0x8971: 65, - 0x8973: 66, - 0x8975: 67, - 0x8976: 68, - 0x8977: 69, - 0x897B: 70, - 0x8981: 71, - 0x8985: 72, - 0x8989: 73, - 0x8993: 74, - 0x8995: 75, - 0x89A1: 76, - 0x89A2: 77, - 0x89A5: 78, - 0x89A8: 79, - 0x89A9: 80, - 0x89AB: 81, - 0x89AD: 82, - 0x89B0: 83, - 0x89B1: 84, - 0x89B3: 85, - 0x89B5: 86, - 0x89B7: 87, - 0x89B8: 88, - 0x89C1: 89, - 0x89C2: 90, - 0x89C5: 91, - 0x89C9: 92, - 0x89CB: 93, - 0x89D1: 94, - 0x89D3: 95, - 0x89D5: 96, - 0x89D7: 97, - 0x89E1: 98, - 0x89E5: 99, - 0x89E9: 100, - 0x89F3: 101, - 0x89F6: 102, - 0x89F7: 103, - 0x8A41: 104, - 0x8A42: 105, - 0x8A45: 106, - 0x8A49: 107, - 0x8A51: 108, - 0x8A53: 109, - 0x8A55: 110, - 0x8A57: 111, - 0x8A61: 112, - 0x8A65: 113, - 0x8A69: 114, - 0x8A73: 115, - 0x8A75: 116, - 0x8A81: 117, - 0x8A82: 118, - 0x8A85: 119, - 0x8A88: 120, - 0x8A89: 121, - 0x8A8A: 122, - 0x8A8B: 123, - 0x8A90: 124, - 0x8A91: 125, - 0x8A93: 126, - 0x8A95: 127, - 0x8A97: 128, - 0x8A98: 129, - 0x8AA1: 130, - 0x8AA2: 131, - 0x8AA5: 132, - 0x8AA9: 133, - 0x8AB6: 134, - 0x8AB7: 135, - 0x8AC1: 136, - 0x8AD5: 137, - 0x8AE1: 138, - 0x8AE2: 139, - 0x8AE5: 140, - 0x8AE9: 141, - 0x8AF1: 142, - 0x8AF3: 143, - 0x8AF5: 144, - 0x8B41: 145, - 0x8B45: 146, - 0x8B49: 147, - 0x8B61: 148, - 0x8B62: 149, - 0x8B65: 150, - 0x8B68: 151, - 0x8B69: 152, - 0x8B6A: 153, - 0x8B71: 154, - 0x8B73: 155, - 0x8B75: 156, - 0x8B77: 157, - 0x8B81: 158, - 0x8BA1: 159, - 0x8BA2: 160, - 0x8BA5: 161, - 0x8BA8: 162, - 0x8BA9: 163, - 0x8BAB: 164, - 0x8BB1: 165, - 0x8BB3: 166, - 0x8BB5: 167, - 0x8BB7: 168, - 0x8BB8: 169, - 0x8BBC: 170, - 0x8C61: 171, - 0x8C62: 172, - 0x8C63: 173, - 0x8C65: 174, - 0x8C69: 175, - 0x8C6B: 176, - 0x8C71: 177, - 0x8C73: 178, - 0x8C75: 179, - 0x8C76: 180, - 0x8C77: 181, - 0x8C7B: 182, - 0x8C81: 183, - 0x8C82: 184, - 0x8C85: 185, - 0x8C89: 186, - 0x8C91: 187, - 0x8C93: 188, - 0x8C95: 189, - 0x8C96: 190, - 0x8C97: 191, - 0x8CA1: 192, - 0x8CA2: 193, - 0x8CA9: 194, - 0x8CE1: 195, - 0x8CE2: 196, - 0x8CE3: 197, - 0x8CE5: 198, - 0x8CE9: 199, - 0x8CF1: 200, - 0x8CF3: 201, - 0x8CF5: 202, - 0x8CF6: 203, - 0x8CF7: 204, - 0x8D41: 205, - 0x8D42: 206, - 0x8D45: 207, - 0x8D51: 208, - 0x8D55: 209, - 0x8D57: 210, - 0x8D61: 211, - 0x8D65: 212, - 0x8D69: 213, - 0x8D75: 214, - 0x8D76: 215, - 0x8D7B: 216, - 0x8D81: 217, - 0x8DA1: 218, - 0x8DA2: 219, - 0x8DA5: 220, - 0x8DA7: 221, - 0x8DA9: 222, - 0x8DB1: 223, - 0x8DB3: 224, - 0x8DB5: 225, - 0x8DB7: 226, - 0x8DB8: 227, - 0x8DB9: 228, - 0x8DC1: 229, - 0x8DC2: 230, - 0x8DC9: 231, - 0x8DD6: 232, - 0x8DD7: 233, - 0x8DE1: 234, - 0x8DE2: 235, - 0x8DF7: 236, - 0x8E41: 237, - 0x8E45: 238, - 0x8E49: 239, - 0x8E51: 240, - 0x8E53: 241, - 0x8E57: 242, - 0x8E61: 243, - 0x8E81: 244, - 0x8E82: 245, - 0x8E85: 246, - 0x8E89: 247, - 0x8E90: 248, - 0x8E91: 249, - 0x8E93: 250, - 0x8E95: 251, - 0x8E97: 252, - 0x8E98: 253, - 0x8EA1: 254, - 0x8EA9: 255, - 0x8EB6: 256, - 0x8EB7: 257, - 0x8EC1: 258, - 0x8EC2: 259, - 0x8EC5: 260, - 0x8EC9: 261, - 0x8ED1: 262, - 0x8ED3: 263, - 0x8ED6: 264, - 0x8EE1: 265, - 0x8EE5: 266, - 0x8EE9: 267, - 0x8EF1: 268, - 0x8EF3: 269, - 0x8F41: 270, - 0x8F61: 271, - 0x8F62: 272, - 0x8F65: 273, - 0x8F67: 274, - 0x8F69: 275, - 0x8F6B: 276, - 0x8F70: 277, - 0x8F71: 278, - 0x8F73: 279, - 0x8F75: 280, - 0x8F77: 281, - 0x8F7B: 282, - 0x8FA1: 283, - 0x8FA2: 284, - 0x8FA5: 285, - 0x8FA9: 286, - 0x8FB1: 287, - 0x8FB3: 288, - 0x8FB5: 289, - 0x8FB7: 290, - 0x9061: 291, - 0x9062: 292, - 0x9063: 293, - 0x9065: 294, - 0x9068: 295, - 0x9069: 296, - 0x906A: 297, - 0x906B: 298, - 0x9071: 299, - 0x9073: 300, - 0x9075: 301, - 0x9076: 302, - 0x9077: 303, - 0x9078: 304, - 0x9079: 305, - 0x907B: 306, - 0x907D: 307, - 0x9081: 308, - 0x9082: 309, - 0x9085: 310, - 0x9089: 311, - 0x9091: 312, - 0x9093: 313, - 0x9095: 314, - 0x9096: 315, - 0x9097: 316, - 0x90A1: 317, - 0x90A2: 318, - 0x90A5: 319, - 0x90A9: 320, - 0x90B1: 321, - 0x90B7: 322, - 0x90E1: 323, - 0x90E2: 324, - 0x90E4: 325, - 0x90E5: 326, - 0x90E9: 327, - 0x90EB: 328, - 0x90EC: 329, - 0x90F1: 330, - 0x90F3: 331, - 0x90F5: 332, - 0x90F6: 333, - 0x90F7: 334, - 0x90FD: 335, - 0x9141: 336, - 0x9142: 337, - 0x9145: 338, - 0x9149: 339, - 0x9151: 340, - 0x9153: 341, - 0x9155: 342, - 0x9156: 343, - 0x9157: 344, - 0x9161: 345, - 0x9162: 346, - 0x9165: 347, - 0x9169: 348, - 0x9171: 349, - 0x9173: 350, - 0x9176: 351, - 0x9177: 352, - 0x917A: 353, - 0x9181: 354, - 0x9185: 355, - 0x91A1: 356, - 0x91A2: 357, - 0x91A5: 358, - 0x91A9: 359, - 0x91AB: 360, - 0x91B1: 361, - 0x91B3: 362, - 0x91B5: 363, - 0x91B7: 364, - 0x91BC: 365, - 0x91BD: 366, - 0x91C1: 367, - 0x91C5: 368, - 0x91C9: 369, - 0x91D6: 370, - 0x9241: 371, - 0x9245: 372, - 0x9249: 373, - 0x9251: 374, - 0x9253: 375, - 0x9255: 376, - 0x9261: 377, - 0x9262: 378, - 0x9265: 379, - 0x9269: 380, - 0x9273: 381, - 0x9275: 382, - 0x9277: 383, - 0x9281: 384, - 0x9282: 385, - 0x9285: 386, - 0x9288: 387, - 0x9289: 388, - 0x9291: 389, - 0x9293: 390, - 0x9295: 391, - 0x9297: 392, - 0x92A1: 393, - 0x92B6: 394, - 0x92C1: 395, - 0x92E1: 396, - 0x92E5: 397, - 0x92E9: 398, - 0x92F1: 399, - 0x92F3: 400, - 0x9341: 401, - 0x9342: 402, - 0x9349: 403, - 0x9351: 404, - 0x9353: 405, - 0x9357: 406, - 0x9361: 407, - 0x9362: 408, - 0x9365: 409, - 0x9369: 410, - 0x936A: 411, - 0x936B: 412, - 0x9371: 413, - 0x9373: 414, - 0x9375: 415, - 0x9377: 416, - 0x9378: 417, - 0x937C: 418, - 0x9381: 419, - 0x9385: 420, - 0x9389: 421, - 0x93A1: 422, - 0x93A2: 423, - 0x93A5: 424, - 0x93A9: 425, - 0x93AB: 426, - 0x93B1: 427, - 0x93B3: 428, - 0x93B5: 429, - 0x93B7: 430, - 0x93BC: 431, - 0x9461: 432, - 0x9462: 433, - 0x9463: 434, - 0x9465: 435, - 0x9468: 436, - 0x9469: 437, - 0x946A: 438, - 0x946B: 439, - 0x946C: 440, - 0x9470: 441, - 0x9471: 442, - 0x9473: 443, - 0x9475: 444, - 0x9476: 445, - 0x9477: 446, - 0x9478: 447, - 0x9479: 448, - 0x947D: 449, - 0x9481: 450, - 0x9482: 451, - 0x9485: 452, - 0x9489: 453, - 0x9491: 454, - 0x9493: 455, - 0x9495: 456, - 0x9496: 457, - 0x9497: 458, - 0x94A1: 459, - 0x94E1: 460, - 0x94E2: 461, - 0x94E3: 462, - 0x94E5: 463, - 0x94E8: 464, - 0x94E9: 465, - 0x94EB: 466, - 0x94EC: 467, - 0x94F1: 468, - 0x94F3: 469, - 0x94F5: 470, - 0x94F7: 471, - 0x94F9: 472, - 0x94FC: 473, - 0x9541: 474, - 0x9542: 475, - 0x9545: 476, - 0x9549: 477, - 0x9551: 478, - 0x9553: 479, - 0x9555: 480, - 0x9556: 481, - 0x9557: 482, - 0x9561: 483, - 0x9565: 484, - 0x9569: 485, - 0x9576: 486, - 0x9577: 487, - 0x9581: 488, - 0x9585: 489, - 0x95A1: 490, - 0x95A2: 491, - 0x95A5: 492, - 0x95A8: 493, - 0x95A9: 494, - 0x95AB: 495, - 0x95AD: 496, - 0x95B1: 497, - 0x95B3: 498, - 0x95B5: 499, - 0x95B7: 500, - 0x95B9: 501, - 0x95BB: 502, - 0x95C1: 503, - 0x95C5: 504, - 0x95C9: 505, - 0x95E1: 506, - 0x95F6: 507, - 0x9641: 508, - 0x9645: 509, - 0x9649: 510, - 0x9651: 511, - 0x9653: 512, - 0x9655: 513, - 0x9661: 514, - 0x9681: 515, - 0x9682: 516, - 0x9685: 517, - 0x9689: 518, - 0x9691: 519, - 0x9693: 520, - 0x9695: 521, - 0x9697: 522, - 0x96A1: 523, - 0x96B6: 524, - 0x96C1: 525, - 0x96D7: 526, - 0x96E1: 527, - 0x96E5: 528, - 0x96E9: 529, - 0x96F3: 530, - 0x96F5: 531, - 0x96F7: 532, - 0x9741: 533, - 0x9745: 534, - 0x9749: 535, - 0x9751: 536, - 0x9757: 537, - 0x9761: 538, - 0x9762: 539, - 0x9765: 540, - 0x9768: 541, - 0x9769: 542, - 0x976B: 543, - 0x9771: 544, - 0x9773: 545, - 0x9775: 546, - 0x9777: 547, - 0x9781: 548, - 0x97A1: 549, - 0x97A2: 550, - 0x97A5: 551, - 0x97A8: 552, - 0x97A9: 553, - 0x97B1: 554, - 0x97B3: 555, - 0x97B5: 556, - 0x97B6: 557, - 0x97B7: 558, - 0x97B8: 559, - 0x9861: 560, - 0x9862: 561, - 0x9865: 562, - 0x9869: 563, - 0x9871: 564, - 0x9873: 565, - 0x9875: 566, - 0x9876: 567, - 0x9877: 568, - 0x987D: 569, - 0x9881: 570, - 0x9882: 571, - 0x9885: 572, - 0x9889: 573, - 0x9891: 574, - 0x9893: 575, - 0x9895: 576, - 0x9896: 577, - 0x9897: 578, - 0x98E1: 579, - 0x98E2: 580, - 0x98E5: 581, - 0x98E9: 582, - 0x98EB: 583, - 0x98EC: 584, - 0x98F1: 585, - 0x98F3: 586, - 0x98F5: 587, - 0x98F6: 588, - 0x98F7: 589, - 0x98FD: 590, - 0x9941: 591, - 0x9942: 592, - 0x9945: 593, - 0x9949: 594, - 0x9951: 595, - 0x9953: 596, - 0x9955: 597, - 0x9956: 598, - 0x9957: 599, - 0x9961: 600, - 0x9976: 601, - 0x99A1: 602, - 0x99A2: 603, - 0x99A5: 604, - 0x99A9: 605, - 0x99B7: 606, - 0x99C1: 607, - 0x99C9: 608, - 0x99E1: 609, - 0x9A41: 610, - 0x9A45: 611, - 0x9A81: 612, - 0x9A82: 613, - 0x9A85: 614, - 0x9A89: 615, - 0x9A90: 616, - 0x9A91: 617, - 0x9A97: 618, - 0x9AC1: 619, - 0x9AE1: 620, - 0x9AE5: 621, - 0x9AE9: 622, - 0x9AF1: 623, - 0x9AF3: 624, - 0x9AF7: 625, - 0x9B61: 626, - 0x9B62: 627, - 0x9B65: 628, - 0x9B68: 629, - 0x9B69: 630, - 0x9B71: 631, - 0x9B73: 632, - 0x9B75: 633, - 0x9B81: 634, - 0x9B85: 635, - 0x9B89: 636, - 0x9B91: 637, - 0x9B93: 638, - 0x9BA1: 639, - 0x9BA5: 640, - 0x9BA9: 641, - 0x9BB1: 642, - 0x9BB3: 643, - 0x9BB5: 644, - 0x9BB7: 645, - 0x9C61: 646, - 0x9C62: 647, - 0x9C65: 648, - 0x9C69: 649, - 0x9C71: 650, - 0x9C73: 651, - 0x9C75: 652, - 0x9C76: 653, - 0x9C77: 654, - 0x9C78: 655, - 0x9C7C: 656, - 0x9C7D: 657, - 0x9C81: 658, - 0x9C82: 659, - 0x9C85: 660, - 0x9C89: 661, - 0x9C91: 662, - 0x9C93: 663, - 0x9C95: 664, - 0x9C96: 665, - 0x9C97: 666, - 0x9CA1: 667, - 0x9CA2: 668, - 0x9CA5: 669, - 0x9CB5: 670, - 0x9CB7: 671, - 0x9CE1: 672, - 0x9CE2: 673, - 0x9CE5: 674, - 0x9CE9: 675, - 0x9CF1: 676, - 0x9CF3: 677, - 0x9CF5: 678, - 0x9CF6: 679, - 0x9CF7: 680, - 0x9CFD: 681, - 0x9D41: 682, - 0x9D42: 683, - 0x9D45: 684, - 0x9D49: 685, - 0x9D51: 686, - 0x9D53: 687, - 0x9D55: 688, - 0x9D57: 689, - 0x9D61: 690, - 0x9D62: 691, - 0x9D65: 692, - 0x9D69: 693, - 0x9D71: 694, - 0x9D73: 695, - 0x9D75: 696, - 0x9D76: 697, - 0x9D77: 698, - 0x9D81: 699, - 0x9D85: 700, - 0x9D93: 701, - 0x9D95: 702, - 0x9DA1: 703, - 0x9DA2: 704, - 0x9DA5: 705, - 0x9DA9: 706, - 0x9DB1: 707, - 0x9DB3: 708, - 0x9DB5: 709, - 0x9DB7: 710, - 0x9DC1: 711, - 0x9DC5: 712, - 0x9DD7: 713, - 0x9DF6: 714, - 0x9E41: 715, - 0x9E45: 716, - 0x9E49: 717, - 0x9E51: 718, - 0x9E53: 719, - 0x9E55: 720, - 0x9E57: 721, - 0x9E61: 722, - 0x9E65: 723, - 0x9E69: 724, - 0x9E73: 725, - 0x9E75: 726, - 0x9E77: 727, - 0x9E81: 728, - 0x9E82: 729, - 0x9E85: 730, - 0x9E89: 731, - 0x9E91: 732, - 0x9E93: 733, - 0x9E95: 734, - 0x9E97: 735, - 0x9EA1: 736, - 0x9EB6: 737, - 0x9EC1: 738, - 0x9EE1: 739, - 0x9EE2: 740, - 0x9EE5: 741, - 0x9EE9: 742, - 0x9EF1: 743, - 0x9EF5: 744, - 0x9EF7: 745, - 0x9F41: 746, - 0x9F42: 747, - 0x9F45: 748, - 0x9F49: 749, - 0x9F51: 750, - 0x9F53: 751, - 0x9F55: 752, - 0x9F57: 753, - 0x9F61: 754, - 0x9F62: 755, - 0x9F65: 756, - 0x9F69: 757, - 0x9F71: 758, - 0x9F73: 759, - 0x9F75: 760, - 0x9F77: 761, - 0x9F78: 762, - 0x9F7B: 763, - 0x9F7C: 764, - 0x9FA1: 765, - 0x9FA2: 766, - 0x9FA5: 767, - 0x9FA9: 768, - 0x9FB1: 769, - 0x9FB3: 770, - 0x9FB5: 771, - 0x9FB7: 772, - 0xA061: 773, - 0xA062: 774, - 0xA065: 775, - 0xA067: 776, - 0xA068: 777, - 0xA069: 778, - 0xA06A: 779, - 0xA06B: 780, - 0xA071: 781, - 0xA073: 782, - 0xA075: 783, - 0xA077: 784, - 0xA078: 785, - 0xA07B: 786, - 0xA07D: 787, - 0xA081: 788, - 0xA082: 789, - 0xA085: 790, - 0xA089: 791, - 0xA091: 792, - 0xA093: 793, - 0xA095: 794, - 0xA096: 795, - 0xA097: 796, - 0xA098: 797, - 0xA0A1: 798, - 0xA0A2: 799, - 0xA0A9: 800, - 0xA0B7: 801, - 0xA0E1: 802, - 0xA0E2: 803, - 0xA0E5: 804, - 0xA0E9: 805, - 0xA0EB: 806, - 0xA0F1: 807, - 0xA0F3: 808, - 0xA0F5: 809, - 0xA0F7: 810, - 0xA0F8: 811, - 0xA0FD: 812, - 0xA141: 813, - 0xA142: 814, - 0xA145: 815, - 0xA149: 816, - 0xA151: 817, - 0xA153: 818, - 0xA155: 819, - 0xA156: 820, - 0xA157: 821, - 0xA161: 822, - 0xA162: 823, - 0xA165: 824, - 0xA169: 825, - 0xA175: 826, - 0xA176: 827, - 0xA177: 828, - 0xA179: 829, - 0xA181: 830, - 0xA1A1: 831, - 0xA1A2: 832, - 0xA1A4: 833, - 0xA1A5: 834, - 0xA1A9: 835, - 0xA1AB: 836, - 0xA1B1: 837, - 0xA1B3: 838, - 0xA1B5: 839, - 0xA1B7: 840, - 0xA1C1: 841, - 0xA1C5: 842, - 0xA1D6: 843, - 0xA1D7: 844, - 0xA241: 845, - 0xA245: 846, - 0xA249: 847, - 0xA253: 848, - 0xA255: 849, - 0xA257: 850, - 0xA261: 851, - 0xA265: 852, - 0xA269: 853, - 0xA273: 854, - 0xA275: 855, - 0xA281: 856, - 0xA282: 857, - 0xA283: 858, - 0xA285: 859, - 0xA288: 860, - 0xA289: 861, - 0xA28A: 862, - 0xA28B: 863, - 0xA291: 864, - 0xA293: 865, - 0xA295: 866, - 0xA297: 867, - 0xA29B: 868, - 0xA29D: 869, - 0xA2A1: 870, - 0xA2A5: 871, - 0xA2A9: 872, - 0xA2B3: 873, - 0xA2B5: 874, - 0xA2C1: 875, - 0xA2E1: 876, - 0xA2E5: 877, - 0xA2E9: 878, - 0xA341: 879, - 0xA345: 880, - 0xA349: 881, - 0xA351: 882, - 0xA355: 883, - 0xA361: 884, - 0xA365: 885, - 0xA369: 886, - 0xA371: 887, - 0xA375: 888, - 0xA3A1: 889, - 0xA3A2: 890, - 0xA3A5: 891, - 0xA3A8: 892, - 0xA3A9: 893, - 0xA3AB: 894, - 0xA3B1: 895, - 0xA3B3: 896, - 0xA3B5: 897, - 0xA3B6: 898, - 0xA3B7: 899, - 0xA3B9: 900, - 0xA3BB: 901, - 0xA461: 902, - 0xA462: 903, - 0xA463: 904, - 0xA464: 905, - 0xA465: 906, - 0xA468: 907, - 0xA469: 908, - 0xA46A: 909, - 0xA46B: 910, - 0xA46C: 911, - 0xA471: 912, - 0xA473: 913, - 0xA475: 914, - 0xA477: 915, - 0xA47B: 916, - 0xA481: 917, - 0xA482: 918, - 0xA485: 919, - 0xA489: 920, - 0xA491: 921, - 0xA493: 922, - 0xA495: 923, - 0xA496: 924, - 0xA497: 925, - 0xA49B: 926, - 0xA4A1: 927, - 0xA4A2: 928, - 0xA4A5: 929, - 0xA4B3: 930, - 0xA4E1: 931, - 0xA4E2: 932, - 0xA4E5: 933, - 0xA4E8: 934, - 0xA4E9: 935, - 0xA4EB: 936, - 0xA4F1: 937, - 0xA4F3: 938, - 0xA4F5: 939, - 0xA4F7: 940, - 0xA4F8: 941, - 0xA541: 942, - 0xA542: 943, - 0xA545: 944, - 0xA548: 945, - 0xA549: 946, - 0xA551: 947, - 0xA553: 948, - 0xA555: 949, - 0xA556: 950, - 0xA557: 951, - 0xA561: 952, - 0xA562: 953, - 0xA565: 954, - 0xA569: 955, - 0xA573: 956, - 0xA575: 957, - 0xA576: 958, - 0xA577: 959, - 0xA57B: 960, - 0xA581: 961, - 0xA585: 962, - 0xA5A1: 963, - 0xA5A2: 964, - 0xA5A3: 965, - 0xA5A5: 966, - 0xA5A9: 967, - 0xA5B1: 968, - 0xA5B3: 969, - 0xA5B5: 970, - 0xA5B7: 971, - 0xA5C1: 972, - 0xA5C5: 973, - 0xA5D6: 974, - 0xA5E1: 975, - 0xA5F6: 976, - 0xA641: 977, - 0xA642: 978, - 0xA645: 979, - 0xA649: 980, - 0xA651: 981, - 0xA653: 982, - 0xA661: 983, - 0xA665: 984, - 0xA681: 985, - 0xA682: 986, - 0xA685: 987, - 0xA688: 988, - 0xA689: 989, - 0xA68A: 990, - 0xA68B: 991, - 0xA691: 992, - 0xA693: 993, - 0xA695: 994, - 0xA697: 995, - 0xA69B: 996, - 0xA69C: 997, - 0xA6A1: 998, - 0xA6A9: 999, - 0xA6B6: 1000, - 0xA6C1: 1001, - 0xA6E1: 1002, - 0xA6E2: 1003, - 0xA6E5: 1004, - 0xA6E9: 1005, - 0xA6F7: 1006, - 0xA741: 1007, - 0xA745: 1008, - 0xA749: 1009, - 0xA751: 1010, - 0xA755: 1011, - 0xA757: 1012, - 0xA761: 1013, - 0xA762: 1014, - 0xA765: 1015, - 0xA769: 1016, - 0xA771: 1017, - 0xA773: 1018, - 0xA775: 1019, - 0xA7A1: 1020, - 0xA7A2: 1021, - 0xA7A5: 1022, - 0xA7A9: 1023, - 0xA7AB: 1024, - 0xA7B1: 1025, - 0xA7B3: 1026, - 0xA7B5: 1027, - 0xA7B7: 1028, - 0xA7B8: 1029, - 0xA7B9: 1030, - 0xA861: 1031, - 0xA862: 1032, - 0xA865: 1033, - 0xA869: 1034, - 0xA86B: 1035, - 0xA871: 1036, - 0xA873: 1037, - 0xA875: 1038, - 0xA876: 1039, - 0xA877: 1040, - 0xA87D: 1041, - 0xA881: 1042, - 0xA882: 1043, - 0xA885: 1044, - 0xA889: 1045, - 0xA891: 1046, - 0xA893: 1047, - 0xA895: 1048, - 0xA896: 1049, - 0xA897: 1050, - 0xA8A1: 1051, - 0xA8A2: 1052, - 0xA8B1: 1053, - 0xA8E1: 1054, - 0xA8E2: 1055, - 0xA8E5: 1056, - 0xA8E8: 1057, - 0xA8E9: 1058, - 0xA8F1: 1059, - 0xA8F5: 1060, - 0xA8F6: 1061, - 0xA8F7: 1062, - 0xA941: 1063, - 0xA957: 1064, - 0xA961: 1065, - 0xA962: 1066, - 0xA971: 1067, - 0xA973: 1068, - 0xA975: 1069, - 0xA976: 1070, - 0xA977: 1071, - 0xA9A1: 1072, - 0xA9A2: 1073, - 0xA9A5: 1074, - 0xA9A9: 1075, - 0xA9B1: 1076, - 0xA9B3: 1077, - 0xA9B7: 1078, - 0xAA41: 1079, - 0xAA61: 1080, - 0xAA77: 1081, - 0xAA81: 1082, - 0xAA82: 1083, - 0xAA85: 1084, - 0xAA89: 1085, - 0xAA91: 1086, - 0xAA95: 1087, - 0xAA97: 1088, - 0xAB41: 1089, - 0xAB57: 1090, - 0xAB61: 1091, - 0xAB65: 1092, - 0xAB69: 1093, - 0xAB71: 1094, - 0xAB73: 1095, - 0xABA1: 1096, - 0xABA2: 1097, - 0xABA5: 1098, - 0xABA9: 1099, - 0xABB1: 1100, - 0xABB3: 1101, - 0xABB5: 1102, - 0xABB7: 1103, - 0xAC61: 1104, - 0xAC62: 1105, - 0xAC64: 1106, - 0xAC65: 1107, - 0xAC68: 1108, - 0xAC69: 1109, - 0xAC6A: 1110, - 0xAC6B: 1111, - 0xAC71: 1112, - 0xAC73: 1113, - 0xAC75: 1114, - 0xAC76: 1115, - 0xAC77: 1116, - 0xAC7B: 1117, - 0xAC81: 1118, - 0xAC82: 1119, - 0xAC85: 1120, - 0xAC89: 1121, - 0xAC91: 1122, - 0xAC93: 1123, - 0xAC95: 1124, - 0xAC96: 1125, - 0xAC97: 1126, - 0xACA1: 1127, - 0xACA2: 1128, - 0xACA5: 1129, - 0xACA9: 1130, - 0xACB1: 1131, - 0xACB3: 1132, - 0xACB5: 1133, - 0xACB7: 1134, - 0xACC1: 1135, - 0xACC5: 1136, - 0xACC9: 1137, - 0xACD1: 1138, - 0xACD7: 1139, - 0xACE1: 1140, - 0xACE2: 1141, - 0xACE3: 1142, - 0xACE4: 1143, - 0xACE5: 1144, - 0xACE8: 1145, - 0xACE9: 1146, - 0xACEB: 1147, - 0xACEC: 1148, - 0xACF1: 1149, - 0xACF3: 1150, - 0xACF5: 1151, - 0xACF6: 1152, - 0xACF7: 1153, - 0xACFC: 1154, - 0xAD41: 1155, - 0xAD42: 1156, - 0xAD45: 1157, - 0xAD49: 1158, - 0xAD51: 1159, - 0xAD53: 1160, - 0xAD55: 1161, - 0xAD56: 1162, - 0xAD57: 1163, - 0xAD61: 1164, - 0xAD62: 1165, - 0xAD65: 1166, - 0xAD69: 1167, - 0xAD71: 1168, - 0xAD73: 1169, - 0xAD75: 1170, - 0xAD76: 1171, - 0xAD77: 1172, - 0xAD81: 1173, - 0xAD85: 1174, - 0xAD89: 1175, - 0xAD97: 1176, - 0xADA1: 1177, - 0xADA2: 1178, - 0xADA3: 1179, - 0xADA5: 1180, - 0xADA9: 1181, - 0xADAB: 1182, - 0xADB1: 1183, - 0xADB3: 1184, - 0xADB5: 1185, - 0xADB7: 1186, - 0xADBB: 1187, - 0xADC1: 1188, - 0xADC2: 1189, - 0xADC5: 1190, - 0xADC9: 1191, - 0xADD7: 1192, - 0xADE1: 1193, - 0xADE5: 1194, - 0xADE9: 1195, - 0xADF1: 1196, - 0xADF5: 1197, - 0xADF6: 1198, - 0xAE41: 1199, - 0xAE45: 1200, - 0xAE49: 1201, - 0xAE51: 1202, - 0xAE53: 1203, - 0xAE55: 1204, - 0xAE61: 1205, - 0xAE62: 1206, - 0xAE65: 1207, - 0xAE69: 1208, - 0xAE71: 1209, - 0xAE73: 1210, - 0xAE75: 1211, - 0xAE77: 1212, - 0xAE81: 1213, - 0xAE82: 1214, - 0xAE85: 1215, - 0xAE88: 1216, - 0xAE89: 1217, - 0xAE91: 1218, - 0xAE93: 1219, - 0xAE95: 1220, - 0xAE97: 1221, - 0xAE99: 1222, - 0xAE9B: 1223, - 0xAE9C: 1224, - 0xAEA1: 1225, - 0xAEB6: 1226, - 0xAEC1: 1227, - 0xAEC2: 1228, - 0xAEC5: 1229, - 0xAEC9: 1230, - 0xAED1: 1231, - 0xAED7: 1232, - 0xAEE1: 1233, - 0xAEE2: 1234, - 0xAEE5: 1235, - 0xAEE9: 1236, - 0xAEF1: 1237, - 0xAEF3: 1238, - 0xAEF5: 1239, - 0xAEF7: 1240, - 0xAF41: 1241, - 0xAF42: 1242, - 0xAF49: 1243, - 0xAF51: 1244, - 0xAF55: 1245, - 0xAF57: 1246, - 0xAF61: 1247, - 0xAF62: 1248, - 0xAF65: 1249, - 0xAF69: 1250, - 0xAF6A: 1251, - 0xAF71: 1252, - 0xAF73: 1253, - 0xAF75: 1254, - 0xAF77: 1255, - 0xAFA1: 1256, - 0xAFA2: 1257, - 0xAFA5: 1258, - 0xAFA8: 1259, - 0xAFA9: 1260, - 0xAFB0: 1261, - 0xAFB1: 1262, - 0xAFB3: 1263, - 0xAFB5: 1264, - 0xAFB7: 1265, - 0xAFBC: 1266, - 0xB061: 1267, - 0xB062: 1268, - 0xB064: 1269, - 0xB065: 1270, - 0xB069: 1271, - 0xB071: 1272, - 0xB073: 1273, - 0xB076: 1274, - 0xB077: 1275, - 0xB07D: 1276, - 0xB081: 1277, - 0xB082: 1278, - 0xB085: 1279, - 0xB089: 1280, - 0xB091: 1281, - 0xB093: 1282, - 0xB096: 1283, - 0xB097: 1284, - 0xB0B7: 1285, - 0xB0E1: 1286, - 0xB0E2: 1287, - 0xB0E5: 1288, - 0xB0E9: 1289, - 0xB0EB: 1290, - 0xB0F1: 1291, - 0xB0F3: 1292, - 0xB0F6: 1293, - 0xB0F7: 1294, - 0xB141: 1295, - 0xB145: 1296, - 0xB149: 1297, - 0xB185: 1298, - 0xB1A1: 1299, - 0xB1A2: 1300, - 0xB1A5: 1301, - 0xB1A8: 1302, - 0xB1A9: 1303, - 0xB1AB: 1304, - 0xB1B1: 1305, - 0xB1B3: 1306, - 0xB1B7: 1307, - 0xB1C1: 1308, - 0xB1C2: 1309, - 0xB1C5: 1310, - 0xB1D6: 1311, - 0xB1E1: 1312, - 0xB1F6: 1313, - 0xB241: 1314, - 0xB245: 1315, - 0xB249: 1316, - 0xB251: 1317, - 0xB253: 1318, - 0xB261: 1319, - 0xB281: 1320, - 0xB282: 1321, - 0xB285: 1322, - 0xB289: 1323, - 0xB291: 1324, - 0xB293: 1325, - 0xB297: 1326, - 0xB2A1: 1327, - 0xB2B6: 1328, - 0xB2C1: 1329, - 0xB2E1: 1330, - 0xB2E5: 1331, - 0xB357: 1332, - 0xB361: 1333, - 0xB362: 1334, - 0xB365: 1335, - 0xB369: 1336, - 0xB36B: 1337, - 0xB370: 1338, - 0xB371: 1339, - 0xB373: 1340, - 0xB381: 1341, - 0xB385: 1342, - 0xB389: 1343, - 0xB391: 1344, - 0xB3A1: 1345, - 0xB3A2: 1346, - 0xB3A5: 1347, - 0xB3A9: 1348, - 0xB3B1: 1349, - 0xB3B3: 1350, - 0xB3B5: 1351, - 0xB3B7: 1352, - 0xB461: 1353, - 0xB462: 1354, - 0xB465: 1355, - 0xB466: 1356, - 0xB467: 1357, - 0xB469: 1358, - 0xB46A: 1359, - 0xB46B: 1360, - 0xB470: 1361, - 0xB471: 1362, - 0xB473: 1363, - 0xB475: 1364, - 0xB476: 1365, - 0xB477: 1366, - 0xB47B: 1367, - 0xB47C: 1368, - 0xB481: 1369, - 0xB482: 1370, - 0xB485: 1371, - 0xB489: 1372, - 0xB491: 1373, - 0xB493: 1374, - 0xB495: 1375, - 0xB496: 1376, - 0xB497: 1377, - 0xB4A1: 1378, - 0xB4A2: 1379, - 0xB4A5: 1380, - 0xB4A9: 1381, - 0xB4AC: 1382, - 0xB4B1: 1383, - 0xB4B3: 1384, - 0xB4B5: 1385, - 0xB4B7: 1386, - 0xB4BB: 1387, - 0xB4BD: 1388, - 0xB4C1: 1389, - 0xB4C5: 1390, - 0xB4C9: 1391, - 0xB4D3: 1392, - 0xB4E1: 1393, - 0xB4E2: 1394, - 0xB4E5: 1395, - 0xB4E6: 1396, - 0xB4E8: 1397, - 0xB4E9: 1398, - 0xB4EA: 1399, - 0xB4EB: 1400, - 0xB4F1: 1401, - 0xB4F3: 1402, - 0xB4F4: 1403, - 0xB4F5: 1404, - 0xB4F6: 1405, - 0xB4F7: 1406, - 0xB4F8: 1407, - 0xB4FA: 1408, - 0xB4FC: 1409, - 0xB541: 1410, - 0xB542: 1411, - 0xB545: 1412, - 0xB549: 1413, - 0xB551: 1414, - 0xB553: 1415, - 0xB555: 1416, - 0xB557: 1417, - 0xB561: 1418, - 0xB562: 1419, - 0xB563: 1420, - 0xB565: 1421, - 0xB569: 1422, - 0xB56B: 1423, - 0xB56C: 1424, - 0xB571: 1425, - 0xB573: 1426, - 0xB574: 1427, - 0xB575: 1428, - 0xB576: 1429, - 0xB577: 1430, - 0xB57B: 1431, - 0xB57C: 1432, - 0xB57D: 1433, - 0xB581: 1434, - 0xB585: 1435, - 0xB589: 1436, - 0xB591: 1437, - 0xB593: 1438, - 0xB595: 1439, - 0xB596: 1440, - 0xB5A1: 1441, - 0xB5A2: 1442, - 0xB5A5: 1443, - 0xB5A9: 1444, - 0xB5AA: 1445, - 0xB5AB: 1446, - 0xB5AD: 1447, - 0xB5B0: 1448, - 0xB5B1: 1449, - 0xB5B3: 1450, - 0xB5B5: 1451, - 0xB5B7: 1452, - 0xB5B9: 1453, - 0xB5C1: 1454, - 0xB5C2: 1455, - 0xB5C5: 1456, - 0xB5C9: 1457, - 0xB5D1: 1458, - 0xB5D3: 1459, - 0xB5D5: 1460, - 0xB5D6: 1461, - 0xB5D7: 1462, - 0xB5E1: 1463, - 0xB5E2: 1464, - 0xB5E5: 1465, - 0xB5F1: 1466, - 0xB5F5: 1467, - 0xB5F7: 1468, - 0xB641: 1469, - 0xB642: 1470, - 0xB645: 1471, - 0xB649: 1472, - 0xB651: 1473, - 0xB653: 1474, - 0xB655: 1475, - 0xB657: 1476, - 0xB661: 1477, - 0xB662: 1478, - 0xB665: 1479, - 0xB669: 1480, - 0xB671: 1481, - 0xB673: 1482, - 0xB675: 1483, - 0xB677: 1484, - 0xB681: 1485, - 0xB682: 1486, - 0xB685: 1487, - 0xB689: 1488, - 0xB68A: 1489, - 0xB68B: 1490, - 0xB691: 1491, - 0xB693: 1492, - 0xB695: 1493, - 0xB697: 1494, - 0xB6A1: 1495, - 0xB6A2: 1496, - 0xB6A5: 1497, - 0xB6A9: 1498, - 0xB6B1: 1499, - 0xB6B3: 1500, - 0xB6B6: 1501, - 0xB6B7: 1502, - 0xB6C1: 1503, - 0xB6C2: 1504, - 0xB6C5: 1505, - 0xB6C9: 1506, - 0xB6D1: 1507, - 0xB6D3: 1508, - 0xB6D7: 1509, - 0xB6E1: 1510, - 0xB6E2: 1511, - 0xB6E5: 1512, - 0xB6E9: 1513, - 0xB6F1: 1514, - 0xB6F3: 1515, - 0xB6F5: 1516, - 0xB6F7: 1517, - 0xB741: 1518, - 0xB742: 1519, - 0xB745: 1520, - 0xB749: 1521, - 0xB751: 1522, - 0xB753: 1523, - 0xB755: 1524, - 0xB757: 1525, - 0xB759: 1526, - 0xB761: 1527, - 0xB762: 1528, - 0xB765: 1529, - 0xB769: 1530, - 0xB76F: 1531, - 0xB771: 1532, - 0xB773: 1533, - 0xB775: 1534, - 0xB777: 1535, - 0xB778: 1536, - 0xB779: 1537, - 0xB77A: 1538, - 0xB77B: 1539, - 0xB77C: 1540, - 0xB77D: 1541, - 0xB781: 1542, - 0xB785: 1543, - 0xB789: 1544, - 0xB791: 1545, - 0xB795: 1546, - 0xB7A1: 1547, - 0xB7A2: 1548, - 0xB7A5: 1549, - 0xB7A9: 1550, - 0xB7AA: 1551, - 0xB7AB: 1552, - 0xB7B0: 1553, - 0xB7B1: 1554, - 0xB7B3: 1555, - 0xB7B5: 1556, - 0xB7B6: 1557, - 0xB7B7: 1558, - 0xB7B8: 1559, - 0xB7BC: 1560, - 0xB861: 1561, - 0xB862: 1562, - 0xB865: 1563, - 0xB867: 1564, - 0xB868: 1565, - 0xB869: 1566, - 0xB86B: 1567, - 0xB871: 1568, - 0xB873: 1569, - 0xB875: 1570, - 0xB876: 1571, - 0xB877: 1572, - 0xB878: 1573, - 0xB881: 1574, - 0xB882: 1575, - 0xB885: 1576, - 0xB889: 1577, - 0xB891: 1578, - 0xB893: 1579, - 0xB895: 1580, - 0xB896: 1581, - 0xB897: 1582, - 0xB8A1: 1583, - 0xB8A2: 1584, - 0xB8A5: 1585, - 0xB8A7: 1586, - 0xB8A9: 1587, - 0xB8B1: 1588, - 0xB8B7: 1589, - 0xB8C1: 1590, - 0xB8C5: 1591, - 0xB8C9: 1592, - 0xB8E1: 1593, - 0xB8E2: 1594, - 0xB8E5: 1595, - 0xB8E9: 1596, - 0xB8EB: 1597, - 0xB8F1: 1598, - 0xB8F3: 1599, - 0xB8F5: 1600, - 0xB8F7: 1601, - 0xB8F8: 1602, - 0xB941: 1603, - 0xB942: 1604, - 0xB945: 1605, - 0xB949: 1606, - 0xB951: 1607, - 0xB953: 1608, - 0xB955: 1609, - 0xB957: 1610, - 0xB961: 1611, - 0xB965: 1612, - 0xB969: 1613, - 0xB971: 1614, - 0xB973: 1615, - 0xB976: 1616, - 0xB977: 1617, - 0xB981: 1618, - 0xB9A1: 1619, - 0xB9A2: 1620, - 0xB9A5: 1621, - 0xB9A9: 1622, - 0xB9AB: 1623, - 0xB9B1: 1624, - 0xB9B3: 1625, - 0xB9B5: 1626, - 0xB9B7: 1627, - 0xB9B8: 1628, - 0xB9B9: 1629, - 0xB9BD: 1630, - 0xB9C1: 1631, - 0xB9C2: 1632, - 0xB9C9: 1633, - 0xB9D3: 1634, - 0xB9D5: 1635, - 0xB9D7: 1636, - 0xB9E1: 1637, - 0xB9F6: 1638, - 0xB9F7: 1639, - 0xBA41: 1640, - 0xBA45: 1641, - 0xBA49: 1642, - 0xBA51: 1643, - 0xBA53: 1644, - 0xBA55: 1645, - 0xBA57: 1646, - 0xBA61: 1647, - 0xBA62: 1648, - 0xBA65: 1649, - 0xBA77: 1650, - 0xBA81: 1651, - 0xBA82: 1652, - 0xBA85: 1653, - 0xBA89: 1654, - 0xBA8A: 1655, - 0xBA8B: 1656, - 0xBA91: 1657, - 0xBA93: 1658, - 0xBA95: 1659, - 0xBA97: 1660, - 0xBAA1: 1661, - 0xBAB6: 1662, - 0xBAC1: 1663, - 0xBAE1: 1664, - 0xBAE2: 1665, - 0xBAE5: 1666, - 0xBAE9: 1667, - 0xBAF1: 1668, - 0xBAF3: 1669, - 0xBAF5: 1670, - 0xBB41: 1671, - 0xBB45: 1672, - 0xBB49: 1673, - 0xBB51: 1674, - 0xBB61: 1675, - 0xBB62: 1676, - 0xBB65: 1677, - 0xBB69: 1678, - 0xBB71: 1679, - 0xBB73: 1680, - 0xBB75: 1681, - 0xBB77: 1682, - 0xBBA1: 1683, - 0xBBA2: 1684, - 0xBBA5: 1685, - 0xBBA8: 1686, - 0xBBA9: 1687, - 0xBBAB: 1688, - 0xBBB1: 1689, - 0xBBB3: 1690, - 0xBBB5: 1691, - 0xBBB7: 1692, - 0xBBB8: 1693, - 0xBBBB: 1694, - 0xBBBC: 1695, - 0xBC61: 1696, - 0xBC62: 1697, - 0xBC65: 1698, - 0xBC67: 1699, - 0xBC69: 1700, - 0xBC6C: 1701, - 0xBC71: 1702, - 0xBC73: 1703, - 0xBC75: 1704, - 0xBC76: 1705, - 0xBC77: 1706, - 0xBC81: 1707, - 0xBC82: 1708, - 0xBC85: 1709, - 0xBC89: 1710, - 0xBC91: 1711, - 0xBC93: 1712, - 0xBC95: 1713, - 0xBC96: 1714, - 0xBC97: 1715, - 0xBCA1: 1716, - 0xBCA5: 1717, - 0xBCB7: 1718, - 0xBCE1: 1719, - 0xBCE2: 1720, - 0xBCE5: 1721, - 0xBCE9: 1722, - 0xBCF1: 1723, - 0xBCF3: 1724, - 0xBCF5: 1725, - 0xBCF6: 1726, - 0xBCF7: 1727, - 0xBD41: 1728, - 0xBD57: 1729, - 0xBD61: 1730, - 0xBD76: 1731, - 0xBDA1: 1732, - 0xBDA2: 1733, - 0xBDA5: 1734, - 0xBDA9: 1735, - 0xBDB1: 1736, - 0xBDB3: 1737, - 0xBDB5: 1738, - 0xBDB7: 1739, - 0xBDB9: 1740, - 0xBDC1: 1741, - 0xBDC2: 1742, - 0xBDC9: 1743, - 0xBDD6: 1744, - 0xBDE1: 1745, - 0xBDF6: 1746, - 0xBE41: 1747, - 0xBE45: 1748, - 0xBE49: 1749, - 0xBE51: 1750, - 0xBE53: 1751, - 0xBE77: 1752, - 0xBE81: 1753, - 0xBE82: 1754, - 0xBE85: 1755, - 0xBE89: 1756, - 0xBE91: 1757, - 0xBE93: 1758, - 0xBE97: 1759, - 0xBEA1: 1760, - 0xBEB6: 1761, - 0xBEB7: 1762, - 0xBEE1: 1763, - 0xBF41: 1764, - 0xBF61: 1765, - 0xBF71: 1766, - 0xBF75: 1767, - 0xBF77: 1768, - 0xBFA1: 1769, - 0xBFA2: 1770, - 0xBFA5: 1771, - 0xBFA9: 1772, - 0xBFB1: 1773, - 0xBFB3: 1774, - 0xBFB7: 1775, - 0xBFB8: 1776, - 0xBFBD: 1777, - 0xC061: 1778, - 0xC062: 1779, - 0xC065: 1780, - 0xC067: 1781, - 0xC069: 1782, - 0xC071: 1783, - 0xC073: 1784, - 0xC075: 1785, - 0xC076: 1786, - 0xC077: 1787, - 0xC078: 1788, - 0xC081: 1789, - 0xC082: 1790, - 0xC085: 1791, - 0xC089: 1792, - 0xC091: 1793, - 0xC093: 1794, - 0xC095: 1795, - 0xC096: 1796, - 0xC097: 1797, - 0xC0A1: 1798, - 0xC0A5: 1799, - 0xC0A7: 1800, - 0xC0A9: 1801, - 0xC0B1: 1802, - 0xC0B7: 1803, - 0xC0E1: 1804, - 0xC0E2: 1805, - 0xC0E5: 1806, - 0xC0E9: 1807, - 0xC0F1: 1808, - 0xC0F3: 1809, - 0xC0F5: 1810, - 0xC0F6: 1811, - 0xC0F7: 1812, - 0xC141: 1813, - 0xC142: 1814, - 0xC145: 1815, - 0xC149: 1816, - 0xC151: 1817, - 0xC153: 1818, - 0xC155: 1819, - 0xC157: 1820, - 0xC161: 1821, - 0xC165: 1822, - 0xC176: 1823, - 0xC181: 1824, - 0xC185: 1825, - 0xC197: 1826, - 0xC1A1: 1827, - 0xC1A2: 1828, - 0xC1A5: 1829, - 0xC1A9: 1830, - 0xC1B1: 1831, - 0xC1B3: 1832, - 0xC1B5: 1833, - 0xC1B7: 1834, - 0xC1C1: 1835, - 0xC1C5: 1836, - 0xC1C9: 1837, - 0xC1D7: 1838, - 0xC241: 1839, - 0xC245: 1840, - 0xC249: 1841, - 0xC251: 1842, - 0xC253: 1843, - 0xC255: 1844, - 0xC257: 1845, - 0xC261: 1846, - 0xC271: 1847, - 0xC281: 1848, - 0xC282: 1849, - 0xC285: 1850, - 0xC289: 1851, - 0xC291: 1852, - 0xC293: 1853, - 0xC295: 1854, - 0xC297: 1855, - 0xC2A1: 1856, - 0xC2B6: 1857, - 0xC2C1: 1858, - 0xC2C5: 1859, - 0xC2E1: 1860, - 0xC2E5: 1861, - 0xC2E9: 1862, - 0xC2F1: 1863, - 0xC2F3: 1864, - 0xC2F5: 1865, - 0xC2F7: 1866, - 0xC341: 1867, - 0xC345: 1868, - 0xC349: 1869, - 0xC351: 1870, - 0xC357: 1871, - 0xC361: 1872, - 0xC362: 1873, - 0xC365: 1874, - 0xC369: 1875, - 0xC371: 1876, - 0xC373: 1877, - 0xC375: 1878, - 0xC377: 1879, - 0xC3A1: 1880, - 0xC3A2: 1881, - 0xC3A5: 1882, - 0xC3A8: 1883, - 0xC3A9: 1884, - 0xC3AA: 1885, - 0xC3B1: 1886, - 0xC3B3: 1887, - 0xC3B5: 1888, - 0xC3B7: 1889, - 0xC461: 1890, - 0xC462: 1891, - 0xC465: 1892, - 0xC469: 1893, - 0xC471: 1894, - 0xC473: 1895, - 0xC475: 1896, - 0xC477: 1897, - 0xC481: 1898, - 0xC482: 1899, - 0xC485: 1900, - 0xC489: 1901, - 0xC491: 1902, - 0xC493: 1903, - 0xC495: 1904, - 0xC496: 1905, - 0xC497: 1906, - 0xC4A1: 1907, - 0xC4A2: 1908, - 0xC4B7: 1909, - 0xC4E1: 1910, - 0xC4E2: 1911, - 0xC4E5: 1912, - 0xC4E8: 1913, - 0xC4E9: 1914, - 0xC4F1: 1915, - 0xC4F3: 1916, - 0xC4F5: 1917, - 0xC4F6: 1918, - 0xC4F7: 1919, - 0xC541: 1920, - 0xC542: 1921, - 0xC545: 1922, - 0xC549: 1923, - 0xC551: 1924, - 0xC553: 1925, - 0xC555: 1926, - 0xC557: 1927, - 0xC561: 1928, - 0xC565: 1929, - 0xC569: 1930, - 0xC571: 1931, - 0xC573: 1932, - 0xC575: 1933, - 0xC576: 1934, - 0xC577: 1935, - 0xC581: 1936, - 0xC5A1: 1937, - 0xC5A2: 1938, - 0xC5A5: 1939, - 0xC5A9: 1940, - 0xC5B1: 1941, - 0xC5B3: 1942, - 0xC5B5: 1943, - 0xC5B7: 1944, - 0xC5C1: 1945, - 0xC5C2: 1946, - 0xC5C5: 1947, - 0xC5C9: 1948, - 0xC5D1: 1949, - 0xC5D7: 1950, - 0xC5E1: 1951, - 0xC5F7: 1952, - 0xC641: 1953, - 0xC649: 1954, - 0xC661: 1955, - 0xC681: 1956, - 0xC682: 1957, - 0xC685: 1958, - 0xC689: 1959, - 0xC691: 1960, - 0xC693: 1961, - 0xC695: 1962, - 0xC697: 1963, - 0xC6A1: 1964, - 0xC6A5: 1965, - 0xC6A9: 1966, - 0xC6B7: 1967, - 0xC6C1: 1968, - 0xC6D7: 1969, - 0xC6E1: 1970, - 0xC6E2: 1971, - 0xC6E5: 1972, - 0xC6E9: 1973, - 0xC6F1: 1974, - 0xC6F3: 1975, - 0xC6F5: 1976, - 0xC6F7: 1977, - 0xC741: 1978, - 0xC745: 1979, - 0xC749: 1980, - 0xC751: 1981, - 0xC761: 1982, - 0xC762: 1983, - 0xC765: 1984, - 0xC769: 1985, - 0xC771: 1986, - 0xC773: 1987, - 0xC777: 1988, - 0xC7A1: 1989, - 0xC7A2: 1990, - 0xC7A5: 1991, - 0xC7A9: 1992, - 0xC7B1: 1993, - 0xC7B3: 1994, - 0xC7B5: 1995, - 0xC7B7: 1996, - 0xC861: 1997, - 0xC862: 1998, - 0xC865: 1999, - 0xC869: 2000, - 0xC86A: 2001, - 0xC871: 2002, - 0xC873: 2003, - 0xC875: 2004, - 0xC876: 2005, - 0xC877: 2006, - 0xC881: 2007, - 0xC882: 2008, - 0xC885: 2009, - 0xC889: 2010, - 0xC891: 2011, - 0xC893: 2012, - 0xC895: 2013, - 0xC896: 2014, - 0xC897: 2015, - 0xC8A1: 2016, - 0xC8B7: 2017, - 0xC8E1: 2018, - 0xC8E2: 2019, - 0xC8E5: 2020, - 0xC8E9: 2021, - 0xC8EB: 2022, - 0xC8F1: 2023, - 0xC8F3: 2024, - 0xC8F5: 2025, - 0xC8F6: 2026, - 0xC8F7: 2027, - 0xC941: 2028, - 0xC942: 2029, - 0xC945: 2030, - 0xC949: 2031, - 0xC951: 2032, - 0xC953: 2033, - 0xC955: 2034, - 0xC957: 2035, - 0xC961: 2036, - 0xC965: 2037, - 0xC976: 2038, - 0xC981: 2039, - 0xC985: 2040, - 0xC9A1: 2041, - 0xC9A2: 2042, - 0xC9A5: 2043, - 0xC9A9: 2044, - 0xC9B1: 2045, - 0xC9B3: 2046, - 0xC9B5: 2047, - 0xC9B7: 2048, - 0xC9BC: 2049, - 0xC9C1: 2050, - 0xC9C5: 2051, - 0xC9E1: 2052, - 0xCA41: 2053, - 0xCA45: 2054, - 0xCA55: 2055, - 0xCA57: 2056, - 0xCA61: 2057, - 0xCA81: 2058, - 0xCA82: 2059, - 0xCA85: 2060, - 0xCA89: 2061, - 0xCA91: 2062, - 0xCA93: 2063, - 0xCA95: 2064, - 0xCA97: 2065, - 0xCAA1: 2066, - 0xCAB6: 2067, - 0xCAC1: 2068, - 0xCAE1: 2069, - 0xCAE2: 2070, - 0xCAE5: 2071, - 0xCAE9: 2072, - 0xCAF1: 2073, - 0xCAF3: 2074, - 0xCAF7: 2075, - 0xCB41: 2076, - 0xCB45: 2077, - 0xCB49: 2078, - 0xCB51: 2079, - 0xCB57: 2080, - 0xCB61: 2081, - 0xCB62: 2082, - 0xCB65: 2083, - 0xCB68: 2084, - 0xCB69: 2085, - 0xCB6B: 2086, - 0xCB71: 2087, - 0xCB73: 2088, - 0xCB75: 2089, - 0xCB81: 2090, - 0xCB85: 2091, - 0xCB89: 2092, - 0xCB91: 2093, - 0xCB93: 2094, - 0xCBA1: 2095, - 0xCBA2: 2096, - 0xCBA5: 2097, - 0xCBA9: 2098, - 0xCBB1: 2099, - 0xCBB3: 2100, - 0xCBB5: 2101, - 0xCBB7: 2102, - 0xCC61: 2103, - 0xCC62: 2104, - 0xCC63: 2105, - 0xCC65: 2106, - 0xCC69: 2107, - 0xCC6B: 2108, - 0xCC71: 2109, - 0xCC73: 2110, - 0xCC75: 2111, - 0xCC76: 2112, - 0xCC77: 2113, - 0xCC7B: 2114, - 0xCC81: 2115, - 0xCC82: 2116, - 0xCC85: 2117, - 0xCC89: 2118, - 0xCC91: 2119, - 0xCC93: 2120, - 0xCC95: 2121, - 0xCC96: 2122, - 0xCC97: 2123, - 0xCCA1: 2124, - 0xCCA2: 2125, - 0xCCE1: 2126, - 0xCCE2: 2127, - 0xCCE5: 2128, - 0xCCE9: 2129, - 0xCCF1: 2130, - 0xCCF3: 2131, - 0xCCF5: 2132, - 0xCCF6: 2133, - 0xCCF7: 2134, - 0xCD41: 2135, - 0xCD42: 2136, - 0xCD45: 2137, - 0xCD49: 2138, - 0xCD51: 2139, - 0xCD53: 2140, - 0xCD55: 2141, - 0xCD57: 2142, - 0xCD61: 2143, - 0xCD65: 2144, - 0xCD69: 2145, - 0xCD71: 2146, - 0xCD73: 2147, - 0xCD76: 2148, - 0xCD77: 2149, - 0xCD81: 2150, - 0xCD89: 2151, - 0xCD93: 2152, - 0xCD95: 2153, - 0xCDA1: 2154, - 0xCDA2: 2155, - 0xCDA5: 2156, - 0xCDA9: 2157, - 0xCDB1: 2158, - 0xCDB3: 2159, - 0xCDB5: 2160, - 0xCDB7: 2161, - 0xCDC1: 2162, - 0xCDD7: 2163, - 0xCE41: 2164, - 0xCE45: 2165, - 0xCE61: 2166, - 0xCE65: 2167, - 0xCE69: 2168, - 0xCE73: 2169, - 0xCE75: 2170, - 0xCE81: 2171, - 0xCE82: 2172, - 0xCE85: 2173, - 0xCE88: 2174, - 0xCE89: 2175, - 0xCE8B: 2176, - 0xCE91: 2177, - 0xCE93: 2178, - 0xCE95: 2179, - 0xCE97: 2180, - 0xCEA1: 2181, - 0xCEB7: 2182, - 0xCEE1: 2183, - 0xCEE5: 2184, - 0xCEE9: 2185, - 0xCEF1: 2186, - 0xCEF5: 2187, - 0xCF41: 2188, - 0xCF45: 2189, - 0xCF49: 2190, - 0xCF51: 2191, - 0xCF55: 2192, - 0xCF57: 2193, - 0xCF61: 2194, - 0xCF65: 2195, - 0xCF69: 2196, - 0xCF71: 2197, - 0xCF73: 2198, - 0xCF75: 2199, - 0xCFA1: 2200, - 0xCFA2: 2201, - 0xCFA5: 2202, - 0xCFA9: 2203, - 0xCFB1: 2204, - 0xCFB3: 2205, - 0xCFB5: 2206, - 0xCFB7: 2207, - 0xD061: 2208, - 0xD062: 2209, - 0xD065: 2210, - 0xD069: 2211, - 0xD06E: 2212, - 0xD071: 2213, - 0xD073: 2214, - 0xD075: 2215, - 0xD077: 2216, - 0xD081: 2217, - 0xD082: 2218, - 0xD085: 2219, - 0xD089: 2220, - 0xD091: 2221, - 0xD093: 2222, - 0xD095: 2223, - 0xD096: 2224, - 0xD097: 2225, - 0xD0A1: 2226, - 0xD0B7: 2227, - 0xD0E1: 2228, - 0xD0E2: 2229, - 0xD0E5: 2230, - 0xD0E9: 2231, - 0xD0EB: 2232, - 0xD0F1: 2233, - 0xD0F3: 2234, - 0xD0F5: 2235, - 0xD0F7: 2236, - 0xD141: 2237, - 0xD142: 2238, - 0xD145: 2239, - 0xD149: 2240, - 0xD151: 2241, - 0xD153: 2242, - 0xD155: 2243, - 0xD157: 2244, - 0xD161: 2245, - 0xD162: 2246, - 0xD165: 2247, - 0xD169: 2248, - 0xD171: 2249, - 0xD173: 2250, - 0xD175: 2251, - 0xD176: 2252, - 0xD177: 2253, - 0xD181: 2254, - 0xD185: 2255, - 0xD189: 2256, - 0xD193: 2257, - 0xD1A1: 2258, - 0xD1A2: 2259, - 0xD1A5: 2260, - 0xD1A9: 2261, - 0xD1AE: 2262, - 0xD1B1: 2263, - 0xD1B3: 2264, - 0xD1B5: 2265, - 0xD1B7: 2266, - 0xD1BB: 2267, - 0xD1C1: 2268, - 0xD1C2: 2269, - 0xD1C5: 2270, - 0xD1C9: 2271, - 0xD1D5: 2272, - 0xD1D7: 2273, - 0xD1E1: 2274, - 0xD1E2: 2275, - 0xD1E5: 2276, - 0xD1F5: 2277, - 0xD1F7: 2278, - 0xD241: 2279, - 0xD242: 2280, - 0xD245: 2281, - 0xD249: 2282, - 0xD253: 2283, - 0xD255: 2284, - 0xD257: 2285, - 0xD261: 2286, - 0xD265: 2287, - 0xD269: 2288, - 0xD273: 2289, - 0xD275: 2290, - 0xD281: 2291, - 0xD282: 2292, - 0xD285: 2293, - 0xD289: 2294, - 0xD28E: 2295, - 0xD291: 2296, - 0xD295: 2297, - 0xD297: 2298, - 0xD2A1: 2299, - 0xD2A5: 2300, - 0xD2A9: 2301, - 0xD2B1: 2302, - 0xD2B7: 2303, - 0xD2C1: 2304, - 0xD2C2: 2305, - 0xD2C5: 2306, - 0xD2C9: 2307, - 0xD2D7: 2308, - 0xD2E1: 2309, - 0xD2E2: 2310, - 0xD2E5: 2311, - 0xD2E9: 2312, - 0xD2F1: 2313, - 0xD2F3: 2314, - 0xD2F5: 2315, - 0xD2F7: 2316, - 0xD341: 2317, - 0xD342: 2318, - 0xD345: 2319, - 0xD349: 2320, - 0xD351: 2321, - 0xD355: 2322, - 0xD357: 2323, - 0xD361: 2324, - 0xD362: 2325, - 0xD365: 2326, - 0xD367: 2327, - 0xD368: 2328, - 0xD369: 2329, - 0xD36A: 2330, - 0xD371: 2331, - 0xD373: 2332, - 0xD375: 2333, - 0xD377: 2334, - 0xD37B: 2335, - 0xD381: 2336, - 0xD385: 2337, - 0xD389: 2338, - 0xD391: 2339, - 0xD393: 2340, - 0xD397: 2341, - 0xD3A1: 2342, - 0xD3A2: 2343, - 0xD3A5: 2344, - 0xD3A9: 2345, - 0xD3B1: 2346, - 0xD3B3: 2347, - 0xD3B5: 2348, - 0xD3B7: 2349, -} diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/johabprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/johabprober.py deleted file mode 100644 index d7364ba..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/johabprober.py +++ /dev/null @@ -1,47 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .chardistribution import JOHABDistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import JOHAB_SM_MODEL - - -class JOHABProber(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(JOHAB_SM_MODEL) - self.distribution_analyzer = JOHABDistributionAnalysis() - self.reset() - - @property - def charset_name(self) -> str: - return "Johab" - - @property - def language(self) -> str: - return "Korean" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/jpcntx.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/jpcntx.py deleted file mode 100644 index 2f53bdd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/jpcntx.py +++ /dev/null @@ -1,238 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Communicator client code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import List, Tuple, Union - -# This is hiragana 2-char sequence table, the number in each cell represents its frequency category -# fmt: off -jp2_char_context = ( - (0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1), - (2, 4, 0, 4, 0, 3, 0, 4, 0, 3, 4, 4, 4, 2, 4, 3, 3, 4, 3, 2, 3, 3, 4, 2, 3, 3, 3, 2, 4, 1, 4, 3, 3, 1, 5, 4, 3, 4, 3, 4, 3, 5, 3, 0, 3, 5, 4, 2, 0, 3, 1, 0, 3, 3, 0, 3, 3, 0, 1, 1, 0, 4, 3, 0, 3, 3, 0, 4, 0, 2, 0, 3, 5, 5, 5, 5, 4, 0, 4, 1, 0, 3, 4), - (0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2), - (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 4, 4, 3, 5, 3, 5, 1, 5, 3, 4, 3, 4, 4, 3, 4, 3, 3, 4, 3, 5, 4, 4, 3, 5, 5, 3, 5, 5, 5, 3, 5, 5, 3, 4, 5, 5, 3, 1, 3, 2, 0, 3, 4, 0, 4, 2, 0, 4, 2, 1, 5, 3, 2, 3, 5, 0, 4, 0, 2, 0, 5, 4, 4, 5, 4, 5, 0, 4, 0, 0, 4, 4), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - (0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 5, 4, 3, 3, 3, 3, 4, 3, 5, 4, 4, 3, 5, 4, 4, 3, 4, 3, 4, 4, 4, 4, 5, 3, 4, 4, 3, 4, 5, 5, 4, 5, 5, 1, 4, 5, 4, 3, 0, 3, 3, 1, 3, 3, 0, 4, 4, 0, 3, 3, 1, 5, 3, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 0, 4, 1, 1, 3, 4), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - (0, 4, 0, 3, 0, 3, 0, 4, 0, 3, 4, 4, 3, 2, 2, 1, 2, 1, 3, 1, 3, 3, 3, 3, 3, 4, 3, 1, 3, 3, 5, 3, 3, 0, 4, 3, 0, 5, 4, 3, 3, 5, 4, 4, 3, 4, 4, 5, 0, 1, 2, 0, 1, 2, 0, 2, 2, 0, 1, 0, 0, 5, 2, 2, 1, 4, 0, 3, 0, 1, 0, 4, 4, 3, 5, 4, 3, 0, 2, 1, 0, 4, 3), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - (0, 3, 0, 5, 0, 4, 0, 2, 1, 4, 4, 2, 4, 1, 4, 2, 4, 2, 4, 3, 3, 3, 4, 3, 3, 3, 3, 1, 4, 2, 3, 3, 3, 1, 4, 4, 1, 1, 1, 4, 3, 3, 2, 0, 2, 4, 3, 2, 0, 3, 3, 0, 3, 1, 1, 0, 0, 0, 3, 3, 0, 4, 2, 2, 3, 4, 0, 4, 0, 3, 0, 4, 4, 5, 3, 4, 4, 0, 3, 0, 0, 1, 4), - (1, 4, 0, 4, 0, 4, 0, 4, 0, 3, 5, 4, 4, 3, 4, 3, 5, 4, 3, 3, 4, 3, 5, 4, 4, 4, 4, 3, 4, 2, 4, 3, 3, 1, 5, 4, 3, 2, 4, 5, 4, 5, 5, 4, 4, 5, 4, 4, 0, 3, 2, 2, 3, 3, 0, 4, 3, 1, 3, 2, 1, 4, 3, 3, 4, 5, 0, 3, 0, 2, 0, 4, 5, 5, 4, 5, 4, 0, 4, 0, 0, 5, 4), - (0, 5, 0, 5, 0, 4, 0, 3, 0, 4, 4, 3, 4, 3, 3, 3, 4, 0, 4, 4, 4, 3, 4, 3, 4, 3, 3, 1, 4, 2, 4, 3, 4, 0, 5, 4, 1, 4, 5, 4, 4, 5, 3, 2, 4, 3, 4, 3, 2, 4, 1, 3, 3, 3, 2, 3, 2, 0, 4, 3, 3, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 4, 3, 0, 4, 1, 0, 1, 3), - (0, 3, 1, 4, 0, 3, 0, 2, 0, 3, 4, 4, 3, 1, 4, 2, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 3, 2, 3, 1, 5, 4, 4, 1, 4, 4, 3, 5, 4, 4, 3, 5, 5, 4, 3, 4, 4, 3, 1, 2, 3, 1, 2, 2, 0, 3, 2, 0, 3, 1, 0, 5, 3, 3, 3, 4, 3, 3, 3, 3, 4, 4, 4, 4, 5, 4, 2, 0, 3, 3, 2, 4, 3), - (0, 2, 0, 3, 0, 1, 0, 1, 0, 0, 3, 2, 0, 0, 2, 0, 1, 0, 2, 1, 3, 3, 3, 1, 2, 3, 1, 0, 1, 0, 4, 2, 1, 1, 3, 3, 0, 4, 3, 3, 1, 4, 3, 3, 0, 3, 3, 2, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 4, 1, 0, 2, 3, 2, 2, 2, 1, 3, 3, 3, 4, 4, 3, 2, 0, 3, 1, 0, 3, 3), - (0, 4, 0, 4, 0, 3, 0, 3, 0, 4, 4, 4, 3, 3, 3, 3, 3, 3, 4, 3, 4, 2, 4, 3, 4, 3, 3, 2, 4, 3, 4, 5, 4, 1, 4, 5, 3, 5, 4, 5, 3, 5, 4, 0, 3, 5, 5, 3, 1, 3, 3, 2, 2, 3, 0, 3, 4, 1, 3, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 5, 4, 4, 5, 3, 0, 4, 1, 0, 3, 4), - (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 2, 2, 1, 0, 1, 0, 0, 0, 3, 0, 3, 0, 3, 0, 1, 3, 1, 0, 3, 1, 3, 3, 3, 1, 3, 3, 3, 0, 1, 3, 1, 3, 4, 0, 0, 3, 1, 1, 0, 3, 2, 0, 0, 0, 0, 1, 3, 0, 1, 0, 0, 3, 3, 2, 0, 3, 0, 0, 0, 0, 0, 3, 4, 3, 4, 3, 3, 0, 3, 0, 0, 2, 3), - (2, 3, 0, 3, 0, 2, 0, 1, 0, 3, 3, 4, 3, 1, 3, 1, 1, 1, 3, 1, 4, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 4, 3, 1, 4, 3, 2, 5, 5, 4, 4, 4, 4, 3, 3, 4, 4, 4, 0, 2, 1, 1, 3, 2, 0, 1, 2, 0, 0, 1, 0, 4, 1, 3, 3, 3, 0, 3, 0, 1, 0, 4, 4, 4, 5, 5, 3, 0, 2, 0, 0, 4, 4), - (0, 2, 0, 1, 0, 3, 1, 3, 0, 2, 3, 3, 3, 0, 3, 1, 0, 0, 3, 0, 3, 2, 3, 1, 3, 2, 1, 1, 0, 0, 4, 2, 1, 0, 2, 3, 1, 4, 3, 2, 0, 4, 4, 3, 1, 3, 1, 3, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 1, 1, 1, 2, 0, 3, 0, 0, 0, 3, 4, 2, 4, 3, 2, 0, 1, 0, 0, 3, 3), - (0, 1, 0, 4, 0, 5, 0, 4, 0, 2, 4, 4, 2, 3, 3, 2, 3, 3, 5, 3, 3, 3, 4, 3, 4, 2, 3, 0, 4, 3, 3, 3, 4, 1, 4, 3, 2, 1, 5, 5, 3, 4, 5, 1, 3, 5, 4, 2, 0, 3, 3, 0, 1, 3, 0, 4, 2, 0, 1, 3, 1, 4, 3, 3, 3, 3, 0, 3, 0, 1, 0, 3, 4, 4, 4, 5, 5, 0, 3, 0, 1, 4, 5), - (0, 2, 0, 3, 0, 3, 0, 0, 0, 2, 3, 1, 3, 0, 4, 0, 1, 1, 3, 0, 3, 4, 3, 2, 3, 1, 0, 3, 3, 2, 3, 1, 3, 0, 2, 3, 0, 2, 1, 4, 1, 2, 2, 0, 0, 3, 3, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 2, 2, 0, 3, 2, 1, 3, 3, 0, 2, 0, 2, 0, 0, 3, 3, 1, 2, 4, 0, 3, 0, 2, 2, 3), - (2, 4, 0, 5, 0, 4, 0, 4, 0, 2, 4, 4, 4, 3, 4, 3, 3, 3, 1, 2, 4, 3, 4, 3, 4, 4, 5, 0, 3, 3, 3, 3, 2, 0, 4, 3, 1, 4, 3, 4, 1, 4, 4, 3, 3, 4, 4, 3, 1, 2, 3, 0, 4, 2, 0, 4, 1, 0, 3, 3, 0, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 3, 5, 3, 4, 5, 2, 0, 3, 0, 0, 4, 5), - (0, 3, 0, 4, 0, 1, 0, 1, 0, 1, 3, 2, 2, 1, 3, 0, 3, 0, 2, 0, 2, 0, 3, 0, 2, 0, 0, 0, 1, 0, 1, 1, 0, 0, 3, 1, 0, 0, 0, 4, 0, 3, 1, 0, 2, 1, 3, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 3, 1, 0, 3, 0, 0, 0, 1, 4, 4, 4, 3, 0, 0, 4, 0, 0, 1, 4), - (1, 4, 1, 5, 0, 3, 0, 3, 0, 4, 5, 4, 4, 3, 5, 3, 3, 4, 4, 3, 4, 1, 3, 3, 3, 3, 2, 1, 4, 1, 5, 4, 3, 1, 4, 4, 3, 5, 4, 4, 3, 5, 4, 3, 3, 4, 4, 4, 0, 3, 3, 1, 2, 3, 0, 3, 1, 0, 3, 3, 0, 5, 4, 4, 4, 4, 4, 4, 3, 3, 5, 4, 4, 3, 3, 5, 4, 0, 3, 2, 0, 4, 4), - (0, 2, 0, 3, 0, 1, 0, 0, 0, 1, 3, 3, 3, 2, 4, 1, 3, 0, 3, 1, 3, 0, 2, 2, 1, 1, 0, 0, 2, 0, 4, 3, 1, 0, 4, 3, 0, 4, 4, 4, 1, 4, 3, 1, 1, 3, 3, 1, 0, 2, 0, 0, 1, 3, 0, 0, 0, 0, 2, 0, 0, 4, 3, 2, 4, 3, 5, 4, 3, 3, 3, 4, 3, 3, 4, 3, 3, 0, 2, 1, 0, 3, 3), - (0, 2, 0, 4, 0, 3, 0, 2, 0, 2, 5, 5, 3, 4, 4, 4, 4, 1, 4, 3, 3, 0, 4, 3, 4, 3, 1, 3, 3, 2, 4, 3, 0, 3, 4, 3, 0, 3, 4, 4, 2, 4, 4, 0, 4, 5, 3, 3, 2, 2, 1, 1, 1, 2, 0, 1, 5, 0, 3, 3, 2, 4, 3, 3, 3, 4, 0, 3, 0, 2, 0, 4, 4, 3, 5, 5, 0, 0, 3, 0, 2, 3, 3), - (0, 3, 0, 4, 0, 3, 0, 1, 0, 3, 4, 3, 3, 1, 3, 3, 3, 0, 3, 1, 3, 0, 4, 3, 3, 1, 1, 0, 3, 0, 3, 3, 0, 0, 4, 4, 0, 1, 5, 4, 3, 3, 5, 0, 3, 3, 4, 3, 0, 2, 0, 1, 1, 1, 0, 1, 3, 0, 1, 2, 1, 3, 3, 2, 3, 3, 0, 3, 0, 1, 0, 1, 3, 3, 4, 4, 1, 0, 1, 2, 2, 1, 3), - (0, 1, 0, 4, 0, 4, 0, 3, 0, 1, 3, 3, 3, 2, 3, 1, 1, 0, 3, 0, 3, 3, 4, 3, 2, 4, 2, 0, 1, 0, 4, 3, 2, 0, 4, 3, 0, 5, 3, 3, 2, 4, 4, 4, 3, 3, 3, 4, 0, 1, 3, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 4, 2, 3, 3, 3, 0, 3, 0, 0, 0, 4, 4, 4, 5, 3, 2, 0, 3, 3, 0, 3, 5), - (0, 2, 0, 3, 0, 0, 0, 3, 0, 1, 3, 0, 2, 0, 0, 0, 1, 0, 3, 1, 1, 3, 3, 0, 0, 3, 0, 0, 3, 0, 2, 3, 1, 0, 3, 1, 0, 3, 3, 2, 0, 4, 2, 2, 0, 2, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 2, 0, 1, 0, 1, 0, 0, 0, 1, 3, 1, 2, 0, 0, 0, 1, 0, 0, 1, 4), - (0, 3, 0, 3, 0, 5, 0, 1, 0, 2, 4, 3, 1, 3, 3, 2, 1, 1, 5, 2, 1, 0, 5, 1, 2, 0, 0, 0, 3, 3, 2, 2, 3, 2, 4, 3, 0, 0, 3, 3, 1, 3, 3, 0, 2, 5, 3, 4, 0, 3, 3, 0, 1, 2, 0, 2, 2, 0, 3, 2, 0, 2, 2, 3, 3, 3, 0, 2, 0, 1, 0, 3, 4, 4, 2, 5, 4, 0, 3, 0, 0, 3, 5), - (0, 3, 0, 3, 0, 3, 0, 1, 0, 3, 3, 3, 3, 0, 3, 0, 2, 0, 2, 1, 1, 0, 2, 0, 1, 0, 0, 0, 2, 1, 0, 0, 1, 0, 3, 2, 0, 0, 3, 3, 1, 2, 3, 1, 0, 3, 3, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 3, 1, 2, 3, 0, 3, 0, 1, 0, 3, 2, 1, 0, 4, 3, 0, 1, 1, 0, 3, 3), - (0, 4, 0, 5, 0, 3, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 4, 3, 5, 3, 3, 2, 5, 3, 4, 4, 4, 3, 4, 3, 4, 5, 5, 3, 4, 4, 3, 4, 4, 5, 4, 4, 4, 3, 4, 5, 5, 4, 2, 3, 4, 2, 3, 4, 0, 3, 3, 1, 4, 3, 2, 4, 3, 3, 5, 5, 0, 3, 0, 3, 0, 5, 5, 5, 5, 4, 4, 0, 4, 0, 1, 4, 4), - (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 5, 4, 4, 2, 3, 2, 5, 1, 3, 2, 5, 1, 4, 2, 3, 2, 3, 3, 4, 3, 3, 3, 3, 2, 5, 4, 1, 3, 3, 5, 3, 4, 4, 0, 4, 4, 3, 1, 1, 3, 1, 0, 2, 3, 0, 2, 3, 0, 3, 0, 0, 4, 3, 1, 3, 4, 0, 3, 0, 2, 0, 4, 4, 4, 3, 4, 5, 0, 4, 0, 0, 3, 4), - (0, 3, 0, 3, 0, 3, 1, 2, 0, 3, 4, 4, 3, 3, 3, 0, 2, 2, 4, 3, 3, 1, 3, 3, 3, 1, 1, 0, 3, 1, 4, 3, 2, 3, 4, 4, 2, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 3, 1, 3, 3, 1, 3, 3, 0, 4, 1, 0, 2, 2, 1, 4, 3, 2, 3, 3, 5, 4, 3, 3, 5, 4, 4, 3, 3, 0, 4, 0, 3, 2, 2, 4, 4), - (0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 2, 1, 3, 0, 0, 0, 0, 0, 2, 0, 1, 2, 1, 0, 0, 1, 0, 0, 0, 0, 3, 0, 0, 1, 0, 1, 1, 3, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 0, 3, 4, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1), - (0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 4, 1, 4, 0, 3, 0, 4, 0, 3, 0, 4, 0, 3, 0, 3, 0, 4, 1, 5, 1, 4, 0, 0, 3, 0, 5, 0, 5, 2, 0, 1, 0, 0, 0, 2, 1, 4, 0, 1, 3, 0, 0, 3, 0, 0, 3, 1, 1, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0), - (1, 4, 0, 5, 0, 3, 0, 2, 0, 3, 5, 4, 4, 3, 4, 3, 5, 3, 4, 3, 3, 0, 4, 3, 3, 3, 3, 3, 3, 2, 4, 4, 3, 1, 3, 4, 4, 5, 4, 4, 3, 4, 4, 1, 3, 5, 4, 3, 3, 3, 1, 2, 2, 3, 3, 1, 3, 1, 3, 3, 3, 5, 3, 3, 4, 5, 0, 3, 0, 3, 0, 3, 4, 3, 4, 4, 3, 0, 3, 0, 2, 4, 3), - (0, 1, 0, 4, 0, 0, 0, 0, 0, 1, 4, 0, 4, 1, 4, 2, 4, 0, 3, 0, 1, 0, 1, 0, 0, 0, 0, 0, 2, 0, 3, 1, 1, 1, 0, 3, 0, 0, 0, 1, 2, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 3, 2, 0, 2, 2, 0, 1, 0, 0, 0, 2, 3, 2, 3, 3, 0, 0, 0, 0, 2, 1, 0), - (0, 5, 1, 5, 0, 3, 0, 3, 0, 5, 4, 4, 5, 1, 5, 3, 3, 0, 4, 3, 4, 3, 5, 3, 4, 3, 3, 2, 4, 3, 4, 3, 3, 0, 3, 3, 1, 4, 4, 3, 4, 4, 4, 3, 4, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 3, 1, 1, 3, 3, 2, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 4, 4, 3, 5, 3, 3, 0, 3, 4, 0, 4, 3), - (0, 5, 0, 5, 0, 3, 0, 2, 0, 4, 4, 3, 5, 2, 4, 3, 3, 3, 4, 4, 4, 3, 5, 3, 5, 3, 3, 1, 4, 0, 4, 3, 3, 0, 3, 3, 0, 4, 4, 4, 4, 5, 4, 3, 3, 5, 5, 3, 2, 3, 1, 2, 3, 2, 0, 1, 0, 0, 3, 2, 2, 4, 4, 3, 1, 5, 0, 4, 0, 3, 0, 4, 3, 1, 3, 2, 1, 0, 3, 3, 0, 3, 3), - (0, 4, 0, 5, 0, 5, 0, 4, 0, 4, 5, 5, 5, 3, 4, 3, 3, 2, 5, 4, 4, 3, 5, 3, 5, 3, 4, 0, 4, 3, 4, 4, 3, 2, 4, 4, 3, 4, 5, 4, 4, 5, 5, 0, 3, 5, 5, 4, 1, 3, 3, 2, 3, 3, 1, 3, 1, 0, 4, 3, 1, 4, 4, 3, 4, 5, 0, 4, 0, 2, 0, 4, 3, 4, 4, 3, 3, 0, 4, 0, 0, 5, 5), - (0, 4, 0, 4, 0, 5, 0, 1, 1, 3, 3, 4, 4, 3, 4, 1, 3, 0, 5, 1, 3, 0, 3, 1, 3, 1, 1, 0, 3, 0, 3, 3, 4, 0, 4, 3, 0, 4, 4, 4, 3, 4, 4, 0, 3, 5, 4, 1, 0, 3, 0, 0, 2, 3, 0, 3, 1, 0, 3, 1, 0, 3, 2, 1, 3, 5, 0, 3, 0, 1, 0, 3, 2, 3, 3, 4, 4, 0, 2, 2, 0, 4, 4), - (2, 4, 0, 5, 0, 4, 0, 3, 0, 4, 5, 5, 4, 3, 5, 3, 5, 3, 5, 3, 5, 2, 5, 3, 4, 3, 3, 4, 3, 4, 5, 3, 2, 1, 5, 4, 3, 2, 3, 4, 5, 3, 4, 1, 2, 5, 4, 3, 0, 3, 3, 0, 3, 2, 0, 2, 3, 0, 4, 1, 0, 3, 4, 3, 3, 5, 0, 3, 0, 1, 0, 4, 5, 5, 5, 4, 3, 0, 4, 2, 0, 3, 5), - (0, 5, 0, 4, 0, 4, 0, 2, 0, 5, 4, 3, 4, 3, 4, 3, 3, 3, 4, 3, 4, 2, 5, 3, 5, 3, 4, 1, 4, 3, 4, 4, 4, 0, 3, 5, 0, 4, 4, 4, 4, 5, 3, 1, 3, 4, 5, 3, 3, 3, 3, 3, 3, 3, 0, 2, 2, 0, 3, 3, 2, 4, 3, 3, 3, 5, 3, 4, 1, 3, 3, 5, 3, 2, 0, 0, 0, 0, 4, 3, 1, 3, 3), - (0, 1, 0, 3, 0, 3, 0, 1, 0, 1, 3, 3, 3, 2, 3, 3, 3, 0, 3, 0, 0, 0, 3, 1, 3, 0, 0, 0, 2, 2, 2, 3, 0, 0, 3, 2, 0, 1, 2, 4, 1, 3, 3, 0, 0, 3, 3, 3, 0, 1, 0, 0, 2, 1, 0, 0, 3, 0, 3, 1, 0, 3, 0, 0, 1, 3, 0, 2, 0, 1, 0, 3, 3, 1, 3, 3, 0, 0, 1, 1, 0, 3, 3), - (0, 2, 0, 3, 0, 2, 1, 4, 0, 2, 2, 3, 1, 1, 3, 1, 1, 0, 2, 0, 3, 1, 2, 3, 1, 3, 0, 0, 1, 0, 4, 3, 2, 3, 3, 3, 1, 4, 2, 3, 3, 3, 3, 1, 0, 3, 1, 4, 0, 1, 1, 0, 1, 2, 0, 1, 1, 0, 1, 1, 0, 3, 1, 3, 2, 2, 0, 1, 0, 0, 0, 2, 3, 3, 3, 1, 0, 0, 0, 0, 0, 2, 3), - (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 5, 5, 3, 3, 4, 3, 3, 1, 5, 4, 4, 2, 4, 4, 4, 3, 4, 2, 4, 3, 5, 5, 4, 3, 3, 4, 3, 3, 5, 5, 4, 5, 5, 1, 3, 4, 5, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 1, 4, 5, 3, 3, 5, 0, 4, 0, 3, 0, 5, 3, 3, 1, 4, 3, 0, 4, 0, 1, 5, 3), - (0, 5, 0, 5, 0, 4, 0, 2, 0, 4, 4, 3, 4, 3, 3, 3, 3, 3, 5, 4, 4, 4, 4, 4, 4, 5, 3, 3, 5, 2, 4, 4, 4, 3, 4, 4, 3, 3, 4, 4, 5, 5, 3, 3, 4, 3, 4, 3, 3, 4, 3, 3, 3, 3, 1, 2, 2, 1, 4, 3, 3, 5, 4, 4, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 4, 4, 1, 0, 4, 2, 0, 2, 4), - (0, 4, 0, 4, 0, 3, 0, 1, 0, 3, 5, 2, 3, 0, 3, 0, 2, 1, 4, 2, 3, 3, 4, 1, 4, 3, 3, 2, 4, 1, 3, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 5, 3, 3, 3, 3, 3, 2, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 3, 1, 2, 2, 3, 0, 3, 0, 2, 0, 4, 4, 3, 3, 4, 1, 0, 3, 0, 0, 2, 4), - (0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 3, 1, 3, 0, 3, 2, 0, 0, 0, 1, 0, 3, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, 0, 2, 0, 0, 0, 0, 0, 0, 2), - (0, 2, 1, 3, 0, 2, 0, 2, 0, 3, 3, 3, 3, 1, 3, 1, 3, 3, 3, 3, 3, 3, 4, 2, 2, 1, 2, 1, 4, 0, 4, 3, 1, 3, 3, 3, 2, 4, 3, 5, 4, 3, 3, 3, 3, 3, 3, 3, 0, 1, 3, 0, 2, 0, 0, 1, 0, 0, 1, 0, 0, 4, 2, 0, 2, 3, 0, 3, 3, 0, 3, 3, 4, 2, 3, 1, 4, 0, 1, 2, 0, 2, 3), - (0, 3, 0, 3, 0, 1, 0, 3, 0, 2, 3, 3, 3, 0, 3, 1, 2, 0, 3, 3, 2, 3, 3, 2, 3, 2, 3, 1, 3, 0, 4, 3, 2, 0, 3, 3, 1, 4, 3, 3, 2, 3, 4, 3, 1, 3, 3, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 4, 1, 1, 0, 3, 0, 3, 1, 0, 2, 3, 3, 3, 3, 3, 1, 0, 0, 2, 0, 3, 3), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 2, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 3), - (0, 2, 0, 3, 1, 3, 0, 3, 0, 2, 3, 3, 3, 1, 3, 1, 3, 1, 3, 1, 3, 3, 3, 1, 3, 0, 2, 3, 1, 1, 4, 3, 3, 2, 3, 3, 1, 2, 2, 4, 1, 3, 3, 0, 1, 4, 2, 3, 0, 1, 3, 0, 3, 0, 0, 1, 3, 0, 2, 0, 0, 3, 3, 2, 1, 3, 0, 3, 0, 2, 0, 3, 4, 4, 4, 3, 1, 0, 3, 0, 0, 3, 3), - (0, 2, 0, 1, 0, 2, 0, 0, 0, 1, 3, 2, 2, 1, 3, 0, 1, 1, 3, 0, 3, 2, 3, 1, 2, 0, 2, 0, 1, 1, 3, 3, 3, 0, 3, 3, 1, 1, 2, 3, 2, 3, 3, 1, 2, 3, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 2, 1, 2, 1, 3, 0, 3, 0, 0, 0, 3, 4, 4, 4, 3, 2, 0, 2, 0, 0, 2, 4), - (0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 0, 0, 3), - (0, 3, 0, 3, 0, 2, 0, 3, 0, 3, 3, 3, 2, 3, 2, 2, 2, 0, 3, 1, 3, 3, 3, 2, 3, 3, 0, 0, 3, 0, 3, 2, 2, 0, 2, 3, 1, 4, 3, 4, 3, 3, 2, 3, 1, 5, 4, 4, 0, 3, 1, 2, 1, 3, 0, 3, 1, 1, 2, 0, 2, 3, 1, 3, 1, 3, 0, 3, 0, 1, 0, 3, 3, 4, 4, 2, 1, 0, 2, 1, 0, 2, 4), - (0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 4, 2, 5, 1, 4, 0, 2, 0, 2, 1, 3, 1, 4, 0, 2, 1, 0, 0, 2, 1, 4, 1, 1, 0, 3, 3, 0, 5, 1, 3, 2, 3, 3, 1, 0, 3, 2, 3, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 1, 0, 3, 0, 2, 0, 1, 0, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 2, 3), - (0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 3), - (0, 1, 0, 3, 0, 4, 0, 3, 0, 2, 4, 3, 1, 0, 3, 2, 2, 1, 3, 1, 2, 2, 3, 1, 1, 1, 2, 1, 3, 0, 1, 2, 0, 1, 3, 2, 1, 3, 0, 5, 5, 1, 0, 0, 1, 3, 2, 1, 0, 3, 0, 0, 1, 0, 0, 0, 0, 0, 3, 4, 0, 1, 1, 1, 3, 2, 0, 2, 0, 1, 0, 2, 3, 3, 1, 2, 3, 0, 1, 0, 1, 0, 4), - (0, 0, 0, 1, 0, 3, 0, 3, 0, 2, 2, 1, 0, 0, 4, 0, 3, 0, 3, 1, 3, 0, 3, 0, 3, 0, 1, 0, 3, 0, 3, 1, 3, 0, 3, 3, 0, 0, 1, 2, 1, 1, 1, 0, 1, 2, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 1, 2, 0, 0, 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, 1, 4), - (0, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, 1, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 0, 2, 0, 2, 3, 0, 0, 2, 2, 3, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 2, 0, 0, 0, 0, 2, 3), - (2, 4, 0, 5, 0, 5, 0, 4, 0, 3, 4, 3, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 4, 5, 5, 5, 2, 3, 0, 5, 5, 4, 1, 5, 4, 3, 1, 5, 4, 3, 4, 4, 3, 3, 4, 3, 3, 0, 3, 2, 0, 2, 3, 0, 3, 0, 0, 3, 3, 0, 5, 3, 2, 3, 3, 0, 3, 0, 3, 0, 3, 4, 5, 4, 5, 3, 0, 4, 3, 0, 3, 4), - (0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 3, 4, 3, 2, 3, 2, 3, 0, 4, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 2, 4, 3, 3, 1, 3, 4, 3, 4, 4, 4, 3, 4, 4, 3, 2, 4, 4, 1, 0, 2, 0, 0, 1, 1, 0, 2, 0, 0, 3, 1, 0, 5, 3, 2, 1, 3, 0, 3, 0, 1, 2, 4, 3, 2, 4, 3, 3, 0, 3, 2, 0, 4, 4), - (0, 3, 0, 3, 0, 1, 0, 0, 0, 1, 4, 3, 3, 2, 3, 1, 3, 1, 4, 2, 3, 2, 4, 2, 3, 4, 3, 0, 2, 2, 3, 3, 3, 0, 3, 3, 3, 0, 3, 4, 1, 3, 3, 0, 3, 4, 3, 3, 0, 1, 1, 0, 1, 0, 0, 0, 4, 0, 3, 0, 0, 3, 1, 2, 1, 3, 0, 4, 0, 1, 0, 4, 3, 3, 4, 3, 3, 0, 2, 0, 0, 3, 3), - (0, 3, 0, 4, 0, 1, 0, 3, 0, 3, 4, 3, 3, 0, 3, 3, 3, 1, 3, 1, 3, 3, 4, 3, 3, 3, 0, 0, 3, 1, 5, 3, 3, 1, 3, 3, 2, 5, 4, 3, 3, 4, 5, 3, 2, 5, 3, 4, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 0, 4, 2, 2, 1, 3, 0, 3, 0, 2, 0, 4, 4, 3, 5, 3, 2, 0, 1, 1, 0, 3, 4), - (0, 5, 0, 4, 0, 5, 0, 2, 0, 4, 4, 3, 3, 2, 3, 3, 3, 1, 4, 3, 4, 1, 5, 3, 4, 3, 4, 0, 4, 2, 4, 3, 4, 1, 5, 4, 0, 4, 4, 4, 4, 5, 4, 1, 3, 5, 4, 2, 1, 4, 1, 1, 3, 2, 0, 3, 1, 0, 3, 2, 1, 4, 3, 3, 3, 4, 0, 4, 0, 3, 0, 4, 4, 4, 3, 3, 3, 0, 4, 2, 0, 3, 4), - (1, 4, 0, 4, 0, 3, 0, 1, 0, 3, 3, 3, 1, 1, 3, 3, 2, 2, 3, 3, 1, 0, 3, 2, 2, 1, 2, 0, 3, 1, 2, 1, 2, 0, 3, 2, 0, 2, 2, 3, 3, 4, 3, 0, 3, 3, 1, 2, 0, 1, 1, 3, 1, 2, 0, 0, 3, 0, 1, 1, 0, 3, 2, 2, 3, 3, 0, 3, 0, 0, 0, 2, 3, 3, 4, 3, 3, 0, 1, 0, 0, 1, 4), - (0, 4, 0, 4, 0, 4, 0, 0, 0, 3, 4, 4, 3, 1, 4, 2, 3, 2, 3, 3, 3, 1, 4, 3, 4, 0, 3, 0, 4, 2, 3, 3, 2, 2, 5, 4, 2, 1, 3, 4, 3, 4, 3, 1, 3, 3, 4, 2, 0, 2, 1, 0, 3, 3, 0, 0, 2, 0, 3, 1, 0, 4, 4, 3, 4, 3, 0, 4, 0, 1, 0, 2, 4, 4, 4, 4, 4, 0, 3, 2, 0, 3, 3), - (0, 0, 0, 1, 0, 4, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3, 2, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2), - (0, 2, 0, 3, 0, 4, 0, 4, 0, 1, 3, 3, 3, 0, 4, 0, 2, 1, 2, 1, 1, 1, 2, 0, 3, 1, 1, 0, 1, 0, 3, 1, 0, 0, 3, 3, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 2, 2, 0, 3, 1, 0, 0, 1, 0, 1, 1, 0, 1, 2, 0, 3, 0, 0, 0, 0, 1, 0, 0, 3, 3, 4, 3, 1, 0, 1, 0, 3, 0, 2), - (0, 0, 0, 3, 0, 5, 0, 0, 0, 0, 1, 0, 2, 0, 3, 1, 0, 1, 3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 4, 0, 0, 0, 2, 3, 0, 1, 4, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 3), - (0, 2, 0, 5, 0, 5, 0, 1, 0, 2, 4, 3, 3, 2, 5, 1, 3, 2, 3, 3, 3, 0, 4, 1, 2, 0, 3, 0, 4, 0, 2, 2, 1, 1, 5, 3, 0, 0, 1, 4, 2, 3, 2, 0, 3, 3, 3, 2, 0, 2, 4, 1, 1, 2, 0, 1, 1, 0, 3, 1, 0, 1, 3, 1, 2, 3, 0, 2, 0, 0, 0, 1, 3, 5, 4, 4, 4, 0, 3, 0, 0, 1, 3), - (0, 4, 0, 5, 0, 4, 0, 4, 0, 4, 5, 4, 3, 3, 4, 3, 3, 3, 4, 3, 4, 4, 5, 3, 4, 5, 4, 2, 4, 2, 3, 4, 3, 1, 4, 4, 1, 3, 5, 4, 4, 5, 5, 4, 4, 5, 5, 5, 2, 3, 3, 1, 4, 3, 1, 3, 3, 0, 3, 3, 1, 4, 3, 4, 4, 4, 0, 3, 0, 4, 0, 3, 3, 4, 4, 5, 0, 0, 4, 3, 0, 4, 5), - (0, 4, 0, 4, 0, 3, 0, 3, 0, 3, 4, 4, 4, 3, 3, 2, 4, 3, 4, 3, 4, 3, 5, 3, 4, 3, 2, 1, 4, 2, 4, 4, 3, 1, 3, 4, 2, 4, 5, 5, 3, 4, 5, 4, 1, 5, 4, 3, 0, 3, 2, 2, 3, 2, 1, 3, 1, 0, 3, 3, 3, 5, 3, 3, 3, 5, 4, 4, 2, 3, 3, 4, 3, 3, 3, 2, 1, 0, 3, 2, 1, 4, 3), - (0, 4, 0, 5, 0, 4, 0, 3, 0, 3, 5, 5, 3, 2, 4, 3, 4, 0, 5, 4, 4, 1, 4, 4, 4, 3, 3, 3, 4, 3, 5, 5, 2, 3, 3, 4, 1, 2, 5, 5, 3, 5, 5, 2, 3, 5, 5, 4, 0, 3, 2, 0, 3, 3, 1, 1, 5, 1, 4, 1, 0, 4, 3, 2, 3, 5, 0, 4, 0, 3, 0, 5, 4, 3, 4, 3, 0, 0, 4, 1, 0, 4, 4), - (1, 3, 0, 4, 0, 2, 0, 2, 0, 2, 5, 5, 3, 3, 3, 3, 3, 0, 4, 2, 3, 4, 4, 4, 3, 4, 0, 0, 3, 4, 5, 4, 3, 3, 3, 3, 2, 5, 5, 4, 5, 5, 5, 4, 3, 5, 5, 5, 1, 3, 1, 0, 1, 0, 0, 3, 2, 0, 4, 2, 0, 5, 2, 3, 2, 4, 1, 3, 0, 3, 0, 4, 5, 4, 5, 4, 3, 0, 4, 2, 0, 5, 4), - (0, 3, 0, 4, 0, 5, 0, 3, 0, 3, 4, 4, 3, 2, 3, 2, 3, 3, 3, 3, 3, 2, 4, 3, 3, 2, 2, 0, 3, 3, 3, 3, 3, 1, 3, 3, 3, 0, 4, 4, 3, 4, 4, 1, 1, 4, 4, 2, 0, 3, 1, 0, 1, 1, 0, 4, 1, 0, 2, 3, 1, 3, 3, 1, 3, 4, 0, 3, 0, 1, 0, 3, 1, 3, 0, 0, 1, 0, 2, 0, 0, 4, 4), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), - (0, 3, 0, 3, 0, 2, 0, 3, 0, 1, 5, 4, 3, 3, 3, 1, 4, 2, 1, 2, 3, 4, 4, 2, 4, 4, 5, 0, 3, 1, 4, 3, 4, 0, 4, 3, 3, 3, 2, 3, 2, 5, 3, 4, 3, 2, 2, 3, 0, 0, 3, 0, 2, 1, 0, 1, 2, 0, 0, 0, 0, 2, 1, 1, 3, 1, 0, 2, 0, 4, 0, 3, 4, 4, 4, 5, 2, 0, 2, 0, 0, 1, 3), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 4, 2, 1, 1, 0, 1, 0, 3, 2, 0, 0, 3, 1, 1, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 4, 0, 4, 2, 1, 0, 0, 0, 0, 0, 1), - (0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 2, 0, 2, 1, 0, 0, 1, 2, 1, 0, 1, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2), - (0, 4, 0, 4, 0, 4, 0, 3, 0, 4, 4, 3, 4, 2, 4, 3, 2, 0, 4, 4, 4, 3, 5, 3, 5, 3, 3, 2, 4, 2, 4, 3, 4, 3, 1, 4, 0, 2, 3, 4, 4, 4, 3, 3, 3, 4, 4, 4, 3, 4, 1, 3, 4, 3, 2, 1, 2, 1, 3, 3, 3, 4, 4, 3, 3, 5, 0, 4, 0, 3, 0, 4, 3, 3, 3, 2, 1, 0, 3, 0, 0, 3, 3), - (0, 4, 0, 3, 0, 3, 0, 3, 0, 3, 5, 5, 3, 3, 3, 3, 4, 3, 4, 3, 3, 3, 4, 4, 4, 3, 3, 3, 3, 4, 3, 5, 3, 3, 1, 3, 2, 4, 5, 5, 5, 5, 4, 3, 4, 5, 5, 3, 2, 2, 3, 3, 3, 3, 2, 3, 3, 1, 2, 3, 2, 4, 3, 3, 3, 4, 0, 4, 0, 2, 0, 4, 3, 2, 2, 1, 2, 0, 3, 0, 0, 4, 1), -) -# fmt: on - - -class JapaneseContextAnalysis: - NUM_OF_CATEGORY = 6 - DONT_KNOW = -1 - ENOUGH_REL_THRESHOLD = 100 - MAX_REL_THRESHOLD = 1000 - MINIMUM_DATA_THRESHOLD = 4 - - def __init__(self) -> None: - self._total_rel = 0 - self._rel_sample: List[int] = [] - self._need_to_skip_char_num = 0 - self._last_char_order = -1 - self._done = False - self.reset() - - def reset(self) -> None: - self._total_rel = 0 # total sequence received - # category counters, each integer counts sequence in its category - self._rel_sample = [0] * self.NUM_OF_CATEGORY - # if last byte in current buffer is not the last byte of a character, - # we need to know how many bytes to skip in next buffer - self._need_to_skip_char_num = 0 - self._last_char_order = -1 # The order of previous char - # If this flag is set to True, detection is done and conclusion has - # been made - self._done = False - - def feed(self, byte_str: Union[bytes, bytearray], num_bytes: int) -> None: - if self._done: - return - - # The buffer we got is byte oriented, and a character may span in more than one - # buffers. In case the last one or two byte in last buffer is not - # complete, we record how many byte needed to complete that character - # and skip these bytes here. We can choose to record those bytes as - # well and analyse the character once it is complete, but since a - # character will not make much difference, by simply skipping - # this character will simply our logic and improve performance. - i = self._need_to_skip_char_num - while i < num_bytes: - order, char_len = self.get_order(byte_str[i : i + 2]) - i += char_len - if i > num_bytes: - self._need_to_skip_char_num = i - num_bytes - self._last_char_order = -1 - else: - if (order != -1) and (self._last_char_order != -1): - self._total_rel += 1 - if self._total_rel > self.MAX_REL_THRESHOLD: - self._done = True - break - self._rel_sample[ - jp2_char_context[self._last_char_order][order] - ] += 1 - self._last_char_order = order - - def got_enough_data(self) -> bool: - return self._total_rel > self.ENOUGH_REL_THRESHOLD - - def get_confidence(self) -> float: - # This is just one way to calculate confidence. It works well for me. - if self._total_rel > self.MINIMUM_DATA_THRESHOLD: - return (self._total_rel - self._rel_sample[0]) / self._total_rel - return self.DONT_KNOW - - def get_order(self, _: Union[bytes, bytearray]) -> Tuple[int, int]: - return -1, 1 - - -class SJISContextAnalysis(JapaneseContextAnalysis): - def __init__(self) -> None: - super().__init__() - self._charset_name = "SHIFT_JIS" - - @property - def charset_name(self) -> str: - return self._charset_name - - def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]: - if not byte_str: - return -1, 1 - # find out current char's byte length - first_char = byte_str[0] - if (0x81 <= first_char <= 0x9F) or (0xE0 <= first_char <= 0xFC): - char_len = 2 - if (first_char == 0x87) or (0xFA <= first_char <= 0xFC): - self._charset_name = "CP932" - else: - char_len = 1 - - # return its order if it is hiragana - if len(byte_str) > 1: - second_char = byte_str[1] - if (first_char == 202) and (0x9F <= second_char <= 0xF1): - return second_char - 0x9F, char_len - - return -1, char_len - - -class EUCJPContextAnalysis(JapaneseContextAnalysis): - def get_order(self, byte_str: Union[bytes, bytearray]) -> Tuple[int, int]: - if not byte_str: - return -1, 1 - # find out current char's byte length - first_char = byte_str[0] - if (first_char == 0x8E) or (0xA1 <= first_char <= 0xFE): - char_len = 2 - elif first_char == 0x8F: - char_len = 3 - else: - char_len = 1 - - # return its order if it is hiragana - if len(byte_str) > 1: - second_char = byte_str[1] - if (first_char == 0xA4) and (0xA1 <= second_char <= 0xF3): - return second_char - 0xA1, char_len - - return -1, char_len diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langbulgarianmodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langbulgarianmodel.py deleted file mode 100644 index 2f771bb..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langbulgarianmodel.py +++ /dev/null @@ -1,4649 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -BULGARIAN_LANG_MODEL = { - 63: { # 'e' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 1, # 'б' - 9: 1, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 1, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 0, # 'и' - 26: 1, # 'й' - 12: 1, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 1, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 1, # 'с' - 5: 1, # 'т' - 19: 0, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 45: { # '\xad' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 0, # 'Л' - 38: 1, # 'М' - 36: 0, # 'Н' - 41: 1, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 1, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 1, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 0, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 0, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 0, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 31: { # 'А' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 1, # 'А' - 32: 1, # 'Б' - 35: 2, # 'В' - 43: 1, # 'Г' - 37: 2, # 'Д' - 44: 2, # 'Е' - 55: 1, # 'Ж' - 47: 2, # 'З' - 40: 1, # 'И' - 59: 1, # 'Й' - 33: 1, # 'К' - 46: 2, # 'Л' - 38: 1, # 'М' - 36: 2, # 'Н' - 41: 1, # 'О' - 30: 2, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 1, # 'У' - 48: 2, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 2, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 1, # 'а' - 18: 2, # 'б' - 9: 2, # 'в' - 20: 2, # 'г' - 11: 2, # 'д' - 3: 1, # 'е' - 23: 1, # 'ж' - 15: 2, # 'з' - 2: 0, # 'и' - 26: 2, # 'й' - 12: 2, # 'к' - 10: 3, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 0, # 'о' - 13: 2, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 1, # 'у' - 29: 2, # 'ф' - 25: 1, # 'х' - 22: 1, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 32: { # 'Б' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 2, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 2, # 'Д' - 44: 1, # 'Е' - 55: 1, # 'Ж' - 47: 2, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 2, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 1, # 'У' - 48: 2, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 0, # 'Ш' - 57: 1, # 'Щ' - 61: 2, # 'Ъ' - 60: 1, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 2, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 1, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 35: { # 'В' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 2, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 1, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 2, # 'Ф' - 49: 0, # 'Х' - 53: 1, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 2, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 2, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 2, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 2, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 43: { # 'Г' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 2, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 0, # 'П' - 39: 1, # 'Р' - 28: 1, # 'С' - 34: 0, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 1, # 'Щ' - 61: 1, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 1, # 'б' - 9: 1, # 'в' - 20: 0, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 2, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 1, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 37: { # 'Д' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 2, # 'В' - 43: 1, # 'Г' - 37: 2, # 'Д' - 44: 2, # 'Е' - 55: 2, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 2, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 2, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 2, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 44: { # 'Е' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 1, # 'Б' - 35: 2, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 1, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 1, # 'Й' - 33: 2, # 'К' - 46: 2, # 'Л' - 38: 1, # 'М' - 36: 2, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 1, # 'У' - 48: 2, # 'Ф' - 49: 1, # 'Х' - 53: 2, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 1, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 0, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 2, # 'д' - 3: 0, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 0, # 'и' - 26: 1, # 'й' - 12: 2, # 'к' - 10: 2, # 'л' - 14: 2, # 'м' - 6: 2, # 'н' - 4: 0, # 'о' - 13: 1, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 1, # 'т' - 19: 1, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 55: { # 'Ж' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 1, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 47: { # 'З' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 2, # 'Н' - 41: 1, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 1, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 2, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 1, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 1, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 40: { # 'И' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 1, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 2, # 'Е' - 55: 1, # 'Ж' - 47: 2, # 'З' - 40: 1, # 'И' - 59: 1, # 'Й' - 33: 2, # 'К' - 46: 2, # 'Л' - 38: 2, # 'М' - 36: 2, # 'Н' - 41: 1, # 'О' - 30: 1, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 0, # 'У' - 48: 1, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 1, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 2, # 'Я' - 1: 1, # 'а' - 18: 1, # 'б' - 9: 3, # 'в' - 20: 2, # 'г' - 11: 1, # 'д' - 3: 1, # 'е' - 23: 0, # 'ж' - 15: 3, # 'з' - 2: 0, # 'и' - 26: 1, # 'й' - 12: 1, # 'к' - 10: 2, # 'л' - 14: 2, # 'м' - 6: 2, # 'н' - 4: 0, # 'о' - 13: 1, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 0, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 1, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 59: { # 'Й' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 1, # 'С' - 34: 1, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 1, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 0, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 1, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 0, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 2, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 33: { # 'К' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 0, # 'М' - 36: 2, # 'Н' - 41: 2, # 'О' - 30: 2, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 1, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 1, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 2, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 3, # 'р' - 8: 1, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 46: { # 'Л' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 2, # 'Г' - 37: 1, # 'Д' - 44: 2, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 0, # 'Р' - 28: 1, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 1, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 1, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 2, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 38: { # 'М' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 2, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 1, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 2, # 'л' - 14: 0, # 'м' - 6: 2, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 36: { # 'Н' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 2, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 2, # 'Д' - 44: 2, # 'Е' - 55: 1, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 1, # 'Й' - 33: 2, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 1, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 1, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 2, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 41: { # 'О' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 1, # 'Б' - 35: 2, # 'В' - 43: 1, # 'Г' - 37: 2, # 'Д' - 44: 1, # 'Е' - 55: 1, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 1, # 'Й' - 33: 2, # 'К' - 46: 2, # 'Л' - 38: 2, # 'М' - 36: 2, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 1, # 'Х' - 53: 0, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 1, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 1, # 'а' - 18: 2, # 'б' - 9: 2, # 'в' - 20: 2, # 'г' - 11: 1, # 'д' - 3: 1, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 0, # 'и' - 26: 1, # 'й' - 12: 2, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 0, # 'о' - 13: 2, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 3, # 'т' - 19: 1, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 1, # 'ц' - 21: 2, # 'ч' - 27: 0, # 'ш' - 24: 2, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 30: { # 'П' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 2, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 2, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 3, # 'л' - 14: 0, # 'м' - 6: 1, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 3, # 'р' - 8: 1, # 'с' - 5: 1, # 'т' - 19: 2, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 39: { # 'Р' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 2, # 'Г' - 37: 2, # 'Д' - 44: 2, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 0, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 2, # 'П' - 39: 1, # 'Р' - 28: 1, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 1, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 1, # 'с' - 5: 0, # 'т' - 19: 3, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 28: { # 'С' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 3, # 'А' - 32: 2, # 'Б' - 35: 2, # 'В' - 43: 1, # 'Г' - 37: 2, # 'Д' - 44: 2, # 'Е' - 55: 1, # 'Ж' - 47: 1, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 2, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 2, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 2, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 1, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 2, # 'к' - 10: 3, # 'л' - 14: 2, # 'м' - 6: 1, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 2, # 'р' - 8: 0, # 'с' - 5: 3, # 'т' - 19: 2, # 'у' - 29: 2, # 'ф' - 25: 1, # 'х' - 22: 1, # 'ц' - 21: 1, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 34: { # 'Т' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 2, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 2, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 2, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 2, # 'О' - 30: 1, # 'П' - 39: 2, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 1, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 1, # 'Ъ' - 60: 0, # 'Ю' - 56: 1, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 1, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 1, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 3, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 51: { # 'У' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 1, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 2, # 'Е' - 55: 1, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 0, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 1, # 'С' - 34: 2, # 'Т' - 51: 0, # 'У' - 48: 1, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 1, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 2, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 2, # 'и' - 26: 1, # 'й' - 12: 2, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 2, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 2, # 'с' - 5: 1, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 2, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 48: { # 'Ф' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 2, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 1, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 2, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 2, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 49: { # 'Х' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 1, # 'П' - 39: 1, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 1, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 1, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 0, # 'н' - 4: 2, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 53: { # 'Ц' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 2, # 'И' - 59: 0, # 'Й' - 33: 2, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 1, # 'Р' - 28: 2, # 'С' - 34: 0, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 2, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 1, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 50: { # 'Ч' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 2, # 'А' - 32: 1, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 1, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 2, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 1, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 54: { # 'Ш' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 1, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 1, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 1, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 2, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 2, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 1, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 57: { # 'Щ' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 1, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 1, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 1, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 1, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 61: { # 'Ъ' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 1, # 'Д' - 44: 0, # 'Е' - 55: 1, # 'Ж' - 47: 1, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 2, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 0, # 'О' - 30: 1, # 'П' - 39: 2, # 'Р' - 28: 1, # 'С' - 34: 1, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 1, # 'Х' - 53: 1, # 'Ц' - 50: 1, # 'Ч' - 54: 1, # 'Ш' - 57: 1, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 0, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 0, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 1, # 'л' - 14: 0, # 'м' - 6: 1, # 'н' - 4: 0, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 60: { # 'Ю' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 1, # 'Б' - 35: 0, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 0, # 'Е' - 55: 1, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 0, # 'М' - 36: 1, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 1, # 'Р' - 28: 1, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 1, # 'б' - 9: 1, # 'в' - 20: 2, # 'г' - 11: 1, # 'д' - 3: 0, # 'е' - 23: 2, # 'ж' - 15: 1, # 'з' - 2: 1, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 0, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 1, # 'с' - 5: 1, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 56: { # 'Я' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 1, # 'Б' - 35: 1, # 'В' - 43: 1, # 'Г' - 37: 1, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 1, # 'Л' - 38: 1, # 'М' - 36: 1, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 1, # 'С' - 34: 2, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 1, # 'б' - 9: 1, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 0, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 1, # 'и' - 26: 1, # 'й' - 12: 1, # 'к' - 10: 1, # 'л' - 14: 2, # 'м' - 6: 2, # 'н' - 4: 0, # 'о' - 13: 2, # 'п' - 7: 1, # 'р' - 8: 1, # 'с' - 5: 1, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 1: { # 'а' - 63: 1, # 'e' - 45: 1, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 1, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 1, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 3, # 'з' - 2: 3, # 'и' - 26: 3, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 2, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 3, # 'ф' - 25: 3, # 'х' - 22: 3, # 'ц' - 21: 3, # 'ч' - 27: 3, # 'ш' - 24: 3, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 18: { # 'б' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 3, # 'в' - 20: 1, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 3, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 1, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 0, # 'т' - 19: 3, # 'у' - 29: 0, # 'ф' - 25: 2, # 'х' - 22: 1, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 3, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 9: { # 'в' - 63: 1, # 'e' - 45: 1, # '\xad' - 31: 0, # 'А' - 32: 1, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 0, # 'в' - 20: 2, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 3, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 2, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 3, # 'ч' - 27: 2, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 20: { # 'г' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 3, # 'л' - 14: 1, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 1, # 'п' - 7: 3, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 3, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 11: { # 'д' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 2, # 'б' - 9: 3, # 'в' - 20: 2, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 2, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 1, # 'т' - 19: 3, # 'у' - 29: 1, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 3: { # 'е' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 2, # 'е' - 23: 3, # 'ж' - 15: 3, # 'з' - 2: 2, # 'и' - 26: 3, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 2, # 'у' - 29: 3, # 'ф' - 25: 3, # 'х' - 22: 3, # 'ц' - 21: 3, # 'ч' - 27: 3, # 'ш' - 24: 3, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 23: { # 'ж' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 2, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 3, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 1, # 'с' - 5: 1, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 1, # 'ц' - 21: 1, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 15: { # 'з' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 1, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 2, # 'ш' - 24: 1, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 2: { # 'и' - 63: 1, # 'e' - 45: 1, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 1, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 1, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 1, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 1, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 3, # 'з' - 2: 3, # 'и' - 26: 3, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 2, # 'у' - 29: 3, # 'ф' - 25: 3, # 'х' - 22: 3, # 'ц' - 21: 3, # 'ч' - 27: 3, # 'ш' - 24: 3, # 'щ' - 17: 2, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 26: { # 'й' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 1, # 'а' - 18: 2, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 2, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 2, # 'з' - 2: 1, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 2, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 2, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 1, # 'у' - 29: 2, # 'ф' - 25: 1, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 12: { # 'к' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 1, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 1, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 3, # 'в' - 20: 2, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 2, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 3, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 1, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 3, # 'ц' - 21: 2, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 10: { # 'л' - 63: 1, # 'e' - 45: 1, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 1, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 2, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 1, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 2, # 'п' - 7: 2, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 2, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 2, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 2, # 'ь' - 42: 3, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 14: { # 'м' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 1, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 2, # 'к' - 10: 3, # 'л' - 14: 1, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 1, # 'т' - 19: 3, # 'у' - 29: 2, # 'ф' - 25: 1, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 2, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 6: { # 'н' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 1, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 2, # 'б' - 9: 2, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 2, # 'ж' - 15: 2, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 1, # 'п' - 7: 2, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 3, # 'ф' - 25: 2, # 'х' - 22: 3, # 'ц' - 21: 3, # 'ч' - 27: 2, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 2, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 4: { # 'о' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 2, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 3, # 'з' - 2: 3, # 'и' - 26: 3, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 2, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 2, # 'у' - 29: 3, # 'ф' - 25: 3, # 'х' - 22: 3, # 'ц' - 21: 3, # 'ч' - 27: 3, # 'ш' - 24: 3, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 13: { # 'п' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 1, # 'й' - 12: 2, # 'к' - 10: 3, # 'л' - 14: 1, # 'м' - 6: 2, # 'н' - 4: 3, # 'о' - 13: 1, # 'п' - 7: 3, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 3, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 7: { # 'р' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 3, # 'е' - 23: 3, # 'ж' - 15: 2, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 2, # 'п' - 7: 1, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 2, # 'ф' - 25: 3, # 'х' - 22: 3, # 'ц' - 21: 2, # 'ч' - 27: 3, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 1, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 8: { # 'с' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 2, # 'б' - 9: 3, # 'в' - 20: 2, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 1, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 2, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 2, # 'ш' - 24: 0, # 'щ' - 17: 3, # 'ъ' - 52: 2, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 5: { # 'т' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 2, # 'г' - 11: 2, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 2, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 3, # 'у' - 29: 1, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 2, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 3, # 'ъ' - 52: 2, # 'ь' - 42: 2, # 'ю' - 16: 3, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 19: { # 'у' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 2, # 'е' - 23: 3, # 'ж' - 15: 3, # 'з' - 2: 2, # 'и' - 26: 2, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 2, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 1, # 'у' - 29: 2, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 3, # 'ч' - 27: 3, # 'ш' - 24: 2, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 29: { # 'ф' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 1, # 'в' - 20: 1, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 2, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 2, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 2, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 25: { # 'х' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 3, # 'в' - 20: 0, # 'г' - 11: 1, # 'д' - 3: 2, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 2, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 1, # 'п' - 7: 3, # 'р' - 8: 1, # 'с' - 5: 2, # 'т' - 19: 3, # 'у' - 29: 0, # 'ф' - 25: 1, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 22: { # 'ц' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 2, # 'в' - 20: 1, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 1, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 2, # 'к' - 10: 1, # 'л' - 14: 1, # 'м' - 6: 1, # 'н' - 4: 2, # 'о' - 13: 1, # 'п' - 7: 1, # 'р' - 8: 1, # 'с' - 5: 1, # 'т' - 19: 2, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 1, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 0, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 21: { # 'ч' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 1, # 'б' - 9: 3, # 'в' - 20: 1, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 1, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 2, # 'л' - 14: 2, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 2, # 'р' - 8: 0, # 'с' - 5: 2, # 'т' - 19: 3, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 1, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 27: { # 'ш' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 2, # 'в' - 20: 0, # 'г' - 11: 1, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 3, # 'к' - 10: 2, # 'л' - 14: 1, # 'м' - 6: 3, # 'н' - 4: 2, # 'о' - 13: 2, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 1, # 'т' - 19: 2, # 'у' - 29: 1, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 1, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 2, # 'ъ' - 52: 1, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 24: { # 'щ' - 63: 1, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 3, # 'а' - 18: 0, # 'б' - 9: 1, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 3, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 3, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 2, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 1, # 'р' - 8: 0, # 'с' - 5: 2, # 'т' - 19: 3, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 1, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 2, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 17: { # 'ъ' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 1, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 3, # 'г' - 11: 3, # 'д' - 3: 2, # 'е' - 23: 3, # 'ж' - 15: 3, # 'з' - 2: 1, # 'и' - 26: 2, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 3, # 'о' - 13: 3, # 'п' - 7: 3, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 1, # 'у' - 29: 1, # 'ф' - 25: 2, # 'х' - 22: 2, # 'ц' - 21: 3, # 'ч' - 27: 2, # 'ш' - 24: 3, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 2, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 52: { # 'ь' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 1, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 0, # 'и' - 26: 0, # 'й' - 12: 1, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 1, # 'н' - 4: 3, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 1, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 1, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 1, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 42: { # 'ю' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 1, # 'а' - 18: 2, # 'б' - 9: 1, # 'в' - 20: 2, # 'г' - 11: 2, # 'д' - 3: 1, # 'е' - 23: 2, # 'ж' - 15: 2, # 'з' - 2: 1, # 'и' - 26: 1, # 'й' - 12: 2, # 'к' - 10: 2, # 'л' - 14: 2, # 'м' - 6: 2, # 'н' - 4: 1, # 'о' - 13: 1, # 'п' - 7: 2, # 'р' - 8: 2, # 'с' - 5: 2, # 'т' - 19: 1, # 'у' - 29: 1, # 'ф' - 25: 1, # 'х' - 22: 2, # 'ц' - 21: 3, # 'ч' - 27: 1, # 'ш' - 24: 1, # 'щ' - 17: 1, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 16: { # 'я' - 63: 0, # 'e' - 45: 1, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 3, # 'б' - 9: 3, # 'в' - 20: 2, # 'г' - 11: 3, # 'д' - 3: 2, # 'е' - 23: 1, # 'ж' - 15: 2, # 'з' - 2: 1, # 'и' - 26: 2, # 'й' - 12: 3, # 'к' - 10: 3, # 'л' - 14: 3, # 'м' - 6: 3, # 'н' - 4: 1, # 'о' - 13: 2, # 'п' - 7: 2, # 'р' - 8: 3, # 'с' - 5: 3, # 'т' - 19: 1, # 'у' - 29: 1, # 'ф' - 25: 3, # 'х' - 22: 2, # 'ц' - 21: 1, # 'ч' - 27: 1, # 'ш' - 24: 2, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 1, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 58: { # 'є' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 0, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 0, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 0, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, - 62: { # '№' - 63: 0, # 'e' - 45: 0, # '\xad' - 31: 0, # 'А' - 32: 0, # 'Б' - 35: 0, # 'В' - 43: 0, # 'Г' - 37: 0, # 'Д' - 44: 0, # 'Е' - 55: 0, # 'Ж' - 47: 0, # 'З' - 40: 0, # 'И' - 59: 0, # 'Й' - 33: 0, # 'К' - 46: 0, # 'Л' - 38: 0, # 'М' - 36: 0, # 'Н' - 41: 0, # 'О' - 30: 0, # 'П' - 39: 0, # 'Р' - 28: 0, # 'С' - 34: 0, # 'Т' - 51: 0, # 'У' - 48: 0, # 'Ф' - 49: 0, # 'Х' - 53: 0, # 'Ц' - 50: 0, # 'Ч' - 54: 0, # 'Ш' - 57: 0, # 'Щ' - 61: 0, # 'Ъ' - 60: 0, # 'Ю' - 56: 0, # 'Я' - 1: 0, # 'а' - 18: 0, # 'б' - 9: 0, # 'в' - 20: 0, # 'г' - 11: 0, # 'д' - 3: 0, # 'е' - 23: 0, # 'ж' - 15: 0, # 'з' - 2: 0, # 'и' - 26: 0, # 'й' - 12: 0, # 'к' - 10: 0, # 'л' - 14: 0, # 'м' - 6: 0, # 'н' - 4: 0, # 'о' - 13: 0, # 'п' - 7: 0, # 'р' - 8: 0, # 'с' - 5: 0, # 'т' - 19: 0, # 'у' - 29: 0, # 'ф' - 25: 0, # 'х' - 22: 0, # 'ц' - 21: 0, # 'ч' - 27: 0, # 'ш' - 24: 0, # 'щ' - 17: 0, # 'ъ' - 52: 0, # 'ь' - 42: 0, # 'ю' - 16: 0, # 'я' - 58: 0, # 'є' - 62: 0, # '№' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -ISO_8859_5_BULGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 77, # 'A' - 66: 90, # 'B' - 67: 99, # 'C' - 68: 100, # 'D' - 69: 72, # 'E' - 70: 109, # 'F' - 71: 107, # 'G' - 72: 101, # 'H' - 73: 79, # 'I' - 74: 185, # 'J' - 75: 81, # 'K' - 76: 102, # 'L' - 77: 76, # 'M' - 78: 94, # 'N' - 79: 82, # 'O' - 80: 110, # 'P' - 81: 186, # 'Q' - 82: 108, # 'R' - 83: 91, # 'S' - 84: 74, # 'T' - 85: 119, # 'U' - 86: 84, # 'V' - 87: 96, # 'W' - 88: 111, # 'X' - 89: 187, # 'Y' - 90: 115, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 65, # 'a' - 98: 69, # 'b' - 99: 70, # 'c' - 100: 66, # 'd' - 101: 63, # 'e' - 102: 68, # 'f' - 103: 112, # 'g' - 104: 103, # 'h' - 105: 92, # 'i' - 106: 194, # 'j' - 107: 104, # 'k' - 108: 95, # 'l' - 109: 86, # 'm' - 110: 87, # 'n' - 111: 71, # 'o' - 112: 116, # 'p' - 113: 195, # 'q' - 114: 85, # 'r' - 115: 93, # 's' - 116: 97, # 't' - 117: 113, # 'u' - 118: 196, # 'v' - 119: 197, # 'w' - 120: 198, # 'x' - 121: 199, # 'y' - 122: 200, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 194, # '\x80' - 129: 195, # '\x81' - 130: 196, # '\x82' - 131: 197, # '\x83' - 132: 198, # '\x84' - 133: 199, # '\x85' - 134: 200, # '\x86' - 135: 201, # '\x87' - 136: 202, # '\x88' - 137: 203, # '\x89' - 138: 204, # '\x8a' - 139: 205, # '\x8b' - 140: 206, # '\x8c' - 141: 207, # '\x8d' - 142: 208, # '\x8e' - 143: 209, # '\x8f' - 144: 210, # '\x90' - 145: 211, # '\x91' - 146: 212, # '\x92' - 147: 213, # '\x93' - 148: 214, # '\x94' - 149: 215, # '\x95' - 150: 216, # '\x96' - 151: 217, # '\x97' - 152: 218, # '\x98' - 153: 219, # '\x99' - 154: 220, # '\x9a' - 155: 221, # '\x9b' - 156: 222, # '\x9c' - 157: 223, # '\x9d' - 158: 224, # '\x9e' - 159: 225, # '\x9f' - 160: 81, # '\xa0' - 161: 226, # 'Ё' - 162: 227, # 'Ђ' - 163: 228, # 'Ѓ' - 164: 229, # 'Є' - 165: 230, # 'Ѕ' - 166: 105, # 'І' - 167: 231, # 'Ї' - 168: 232, # 'Ј' - 169: 233, # 'Љ' - 170: 234, # 'Њ' - 171: 235, # 'Ћ' - 172: 236, # 'Ќ' - 173: 45, # '\xad' - 174: 237, # 'Ў' - 175: 238, # 'Џ' - 176: 31, # 'А' - 177: 32, # 'Б' - 178: 35, # 'В' - 179: 43, # 'Г' - 180: 37, # 'Д' - 181: 44, # 'Е' - 182: 55, # 'Ж' - 183: 47, # 'З' - 184: 40, # 'И' - 185: 59, # 'Й' - 186: 33, # 'К' - 187: 46, # 'Л' - 188: 38, # 'М' - 189: 36, # 'Н' - 190: 41, # 'О' - 191: 30, # 'П' - 192: 39, # 'Р' - 193: 28, # 'С' - 194: 34, # 'Т' - 195: 51, # 'У' - 196: 48, # 'Ф' - 197: 49, # 'Х' - 198: 53, # 'Ц' - 199: 50, # 'Ч' - 200: 54, # 'Ш' - 201: 57, # 'Щ' - 202: 61, # 'Ъ' - 203: 239, # 'Ы' - 204: 67, # 'Ь' - 205: 240, # 'Э' - 206: 60, # 'Ю' - 207: 56, # 'Я' - 208: 1, # 'а' - 209: 18, # 'б' - 210: 9, # 'в' - 211: 20, # 'г' - 212: 11, # 'д' - 213: 3, # 'е' - 214: 23, # 'ж' - 215: 15, # 'з' - 216: 2, # 'и' - 217: 26, # 'й' - 218: 12, # 'к' - 219: 10, # 'л' - 220: 14, # 'м' - 221: 6, # 'н' - 222: 4, # 'о' - 223: 13, # 'п' - 224: 7, # 'р' - 225: 8, # 'с' - 226: 5, # 'т' - 227: 19, # 'у' - 228: 29, # 'ф' - 229: 25, # 'х' - 230: 22, # 'ц' - 231: 21, # 'ч' - 232: 27, # 'ш' - 233: 24, # 'щ' - 234: 17, # 'ъ' - 235: 75, # 'ы' - 236: 52, # 'ь' - 237: 241, # 'э' - 238: 42, # 'ю' - 239: 16, # 'я' - 240: 62, # '№' - 241: 242, # 'ё' - 242: 243, # 'ђ' - 243: 244, # 'ѓ' - 244: 58, # 'є' - 245: 245, # 'ѕ' - 246: 98, # 'і' - 247: 246, # 'ї' - 248: 247, # 'ј' - 249: 248, # 'љ' - 250: 249, # 'њ' - 251: 250, # 'ћ' - 252: 251, # 'ќ' - 253: 91, # '§' - 254: 252, # 'ў' - 255: 253, # 'џ' -} - -ISO_8859_5_BULGARIAN_MODEL = SingleByteCharSetModel( - charset_name="ISO-8859-5", - language="Bulgarian", - char_to_order_map=ISO_8859_5_BULGARIAN_CHAR_TO_ORDER, - language_model=BULGARIAN_LANG_MODEL, - typical_positive_ratio=0.969392, - keep_ascii_letters=False, - alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя", -) - -WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 77, # 'A' - 66: 90, # 'B' - 67: 99, # 'C' - 68: 100, # 'D' - 69: 72, # 'E' - 70: 109, # 'F' - 71: 107, # 'G' - 72: 101, # 'H' - 73: 79, # 'I' - 74: 185, # 'J' - 75: 81, # 'K' - 76: 102, # 'L' - 77: 76, # 'M' - 78: 94, # 'N' - 79: 82, # 'O' - 80: 110, # 'P' - 81: 186, # 'Q' - 82: 108, # 'R' - 83: 91, # 'S' - 84: 74, # 'T' - 85: 119, # 'U' - 86: 84, # 'V' - 87: 96, # 'W' - 88: 111, # 'X' - 89: 187, # 'Y' - 90: 115, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 65, # 'a' - 98: 69, # 'b' - 99: 70, # 'c' - 100: 66, # 'd' - 101: 63, # 'e' - 102: 68, # 'f' - 103: 112, # 'g' - 104: 103, # 'h' - 105: 92, # 'i' - 106: 194, # 'j' - 107: 104, # 'k' - 108: 95, # 'l' - 109: 86, # 'm' - 110: 87, # 'n' - 111: 71, # 'o' - 112: 116, # 'p' - 113: 195, # 'q' - 114: 85, # 'r' - 115: 93, # 's' - 116: 97, # 't' - 117: 113, # 'u' - 118: 196, # 'v' - 119: 197, # 'w' - 120: 198, # 'x' - 121: 199, # 'y' - 122: 200, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 206, # 'Ђ' - 129: 207, # 'Ѓ' - 130: 208, # '‚' - 131: 209, # 'ѓ' - 132: 210, # '„' - 133: 211, # '…' - 134: 212, # '†' - 135: 213, # '‡' - 136: 120, # '€' - 137: 214, # '‰' - 138: 215, # 'Љ' - 139: 216, # '‹' - 140: 217, # 'Њ' - 141: 218, # 'Ќ' - 142: 219, # 'Ћ' - 143: 220, # 'Џ' - 144: 221, # 'ђ' - 145: 78, # '‘' - 146: 64, # '’' - 147: 83, # '“' - 148: 121, # '”' - 149: 98, # '•' - 150: 117, # '–' - 151: 105, # '—' - 152: 222, # None - 153: 223, # '™' - 154: 224, # 'љ' - 155: 225, # '›' - 156: 226, # 'њ' - 157: 227, # 'ќ' - 158: 228, # 'ћ' - 159: 229, # 'џ' - 160: 88, # '\xa0' - 161: 230, # 'Ў' - 162: 231, # 'ў' - 163: 232, # 'Ј' - 164: 233, # '¤' - 165: 122, # 'Ґ' - 166: 89, # '¦' - 167: 106, # '§' - 168: 234, # 'Ё' - 169: 235, # '©' - 170: 236, # 'Є' - 171: 237, # '«' - 172: 238, # '¬' - 173: 45, # '\xad' - 174: 239, # '®' - 175: 240, # 'Ї' - 176: 73, # '°' - 177: 80, # '±' - 178: 118, # 'І' - 179: 114, # 'і' - 180: 241, # 'ґ' - 181: 242, # 'µ' - 182: 243, # '¶' - 183: 244, # '·' - 184: 245, # 'ё' - 185: 62, # '№' - 186: 58, # 'є' - 187: 246, # '»' - 188: 247, # 'ј' - 189: 248, # 'Ѕ' - 190: 249, # 'ѕ' - 191: 250, # 'ї' - 192: 31, # 'А' - 193: 32, # 'Б' - 194: 35, # 'В' - 195: 43, # 'Г' - 196: 37, # 'Д' - 197: 44, # 'Е' - 198: 55, # 'Ж' - 199: 47, # 'З' - 200: 40, # 'И' - 201: 59, # 'Й' - 202: 33, # 'К' - 203: 46, # 'Л' - 204: 38, # 'М' - 205: 36, # 'Н' - 206: 41, # 'О' - 207: 30, # 'П' - 208: 39, # 'Р' - 209: 28, # 'С' - 210: 34, # 'Т' - 211: 51, # 'У' - 212: 48, # 'Ф' - 213: 49, # 'Х' - 214: 53, # 'Ц' - 215: 50, # 'Ч' - 216: 54, # 'Ш' - 217: 57, # 'Щ' - 218: 61, # 'Ъ' - 219: 251, # 'Ы' - 220: 67, # 'Ь' - 221: 252, # 'Э' - 222: 60, # 'Ю' - 223: 56, # 'Я' - 224: 1, # 'а' - 225: 18, # 'б' - 226: 9, # 'в' - 227: 20, # 'г' - 228: 11, # 'д' - 229: 3, # 'е' - 230: 23, # 'ж' - 231: 15, # 'з' - 232: 2, # 'и' - 233: 26, # 'й' - 234: 12, # 'к' - 235: 10, # 'л' - 236: 14, # 'м' - 237: 6, # 'н' - 238: 4, # 'о' - 239: 13, # 'п' - 240: 7, # 'р' - 241: 8, # 'с' - 242: 5, # 'т' - 243: 19, # 'у' - 244: 29, # 'ф' - 245: 25, # 'х' - 246: 22, # 'ц' - 247: 21, # 'ч' - 248: 27, # 'ш' - 249: 24, # 'щ' - 250: 17, # 'ъ' - 251: 75, # 'ы' - 252: 52, # 'ь' - 253: 253, # 'э' - 254: 42, # 'ю' - 255: 16, # 'я' -} - -WINDOWS_1251_BULGARIAN_MODEL = SingleByteCharSetModel( - charset_name="windows-1251", - language="Bulgarian", - char_to_order_map=WINDOWS_1251_BULGARIAN_CHAR_TO_ORDER, - language_model=BULGARIAN_LANG_MODEL, - typical_positive_ratio=0.969392, - keep_ascii_letters=False, - alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langgreekmodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langgreekmodel.py deleted file mode 100644 index 0471d8b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langgreekmodel.py +++ /dev/null @@ -1,4397 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -GREEK_LANG_MODEL = { - 60: { # 'e' - 60: 2, # 'e' - 55: 1, # 'o' - 58: 2, # 't' - 36: 1, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 1, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 55: { # 'o' - 60: 0, # 'e' - 55: 2, # 'o' - 58: 2, # 't' - 36: 1, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 1, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 1, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 58: { # 't' - 60: 2, # 'e' - 55: 1, # 'o' - 58: 1, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 1, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 36: { # '·' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 61: { # 'Ά' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 1, # 'γ' - 21: 2, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 1, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 46: { # 'Έ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 2, # 'β' - 20: 2, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 2, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 0, # 'ο' - 9: 2, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 1, # 'σ' - 2: 2, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 3, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 54: { # 'Ό' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 2, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 2, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 2, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 31: { # 'Α' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 2, # 'Β' - 43: 2, # 'Γ' - 41: 1, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 2, # 'Θ' - 47: 2, # 'Ι' - 44: 2, # 'Κ' - 53: 2, # 'Λ' - 38: 2, # 'Μ' - 49: 2, # 'Ν' - 59: 1, # 'Ξ' - 39: 0, # 'Ο' - 35: 2, # 'Π' - 48: 2, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 2, # 'Υ' - 56: 2, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 2, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 1, # 'θ' - 5: 0, # 'ι' - 11: 2, # 'κ' - 16: 3, # 'λ' - 10: 2, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 0, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 2, # 'ς' - 7: 2, # 'σ' - 2: 0, # 'τ' - 12: 3, # 'υ' - 28: 2, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 2, # 'ύ' - 27: 0, # 'ώ' - }, - 51: { # 'Β' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 1, # 'Ε' - 40: 1, # 'Η' - 52: 0, # 'Θ' - 47: 1, # 'Ι' - 44: 0, # 'Κ' - 53: 1, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 2, # 'ή' - 15: 0, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 43: { # 'Γ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 1, # 'Α' - 51: 0, # 'Β' - 43: 2, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 1, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 1, # 'Κ' - 53: 1, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 1, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 2, # 'Υ' - 56: 0, # 'Φ' - 50: 1, # 'Χ' - 57: 2, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 41: { # 'Δ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 2, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 2, # 'ή' - 15: 2, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 1, # 'ό' - 26: 2, # 'ύ' - 27: 2, # 'ώ' - }, - 34: { # 'Ε' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 2, # 'Γ' - 41: 2, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 2, # 'Κ' - 53: 2, # 'Λ' - 38: 2, # 'Μ' - 49: 2, # 'Ν' - 59: 1, # 'Ξ' - 39: 0, # 'Ο' - 35: 2, # 'Π' - 48: 2, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 2, # 'Υ' - 56: 0, # 'Φ' - 50: 2, # 'Χ' - 57: 2, # 'Ω' - 17: 3, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 3, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 3, # 'γ' - 21: 2, # 'δ' - 3: 1, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 1, # 'θ' - 5: 2, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 2, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 0, # 'ο' - 9: 3, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 2, # 'σ' - 2: 2, # 'τ' - 12: 2, # 'υ' - 28: 2, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 1, # 'ύ' - 27: 0, # 'ώ' - }, - 40: { # 'Η' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 1, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 2, # 'Θ' - 47: 0, # 'Ι' - 44: 2, # 'Κ' - 53: 0, # 'Λ' - 38: 2, # 'Μ' - 49: 2, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 2, # 'Π' - 48: 2, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 1, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 1, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 1, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 52: { # 'Θ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 1, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 1, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 2, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 2, # 'ύ' - 27: 0, # 'ώ' - }, - 47: { # 'Ι' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 1, # 'Β' - 43: 1, # 'Γ' - 41: 2, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 2, # 'Κ' - 53: 2, # 'Λ' - 38: 2, # 'Μ' - 49: 2, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 0, # 'Υ' - 56: 2, # 'Φ' - 50: 0, # 'Χ' - 57: 2, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 2, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 1, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 2, # 'σ' - 2: 1, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 1, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 44: { # 'Κ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 1, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 1, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 0, # 'Σ' - 33: 1, # 'Τ' - 45: 2, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 1, # 'Ω' - 17: 3, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 2, # 'ό' - 26: 2, # 'ύ' - 27: 2, # 'ώ' - }, - 53: { # 'Λ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 2, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 2, # 'Σ' - 33: 0, # 'Τ' - 45: 2, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 2, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 0, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 1, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 2, # 'ό' - 26: 2, # 'ύ' - 27: 0, # 'ώ' - }, - 38: { # 'Μ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 2, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 2, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 2, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 2, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 3, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 2, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 49: { # 'Ν' - 60: 2, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 2, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 2, # 'Ω' - 17: 0, # 'ά' - 18: 2, # 'έ' - 22: 0, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 1, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 1, # 'ω' - 19: 2, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 59: { # 'Ξ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 1, # 'Ε' - 40: 1, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 1, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 2, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 39: { # 'Ο' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 1, # 'Β' - 43: 2, # 'Γ' - 41: 2, # 'Δ' - 34: 2, # 'Ε' - 40: 1, # 'Η' - 52: 2, # 'Θ' - 47: 2, # 'Ι' - 44: 2, # 'Κ' - 53: 2, # 'Λ' - 38: 2, # 'Μ' - 49: 2, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 2, # 'Π' - 48: 2, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 2, # 'Υ' - 56: 2, # 'Φ' - 50: 2, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 2, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 2, # 'κ' - 16: 2, # 'λ' - 10: 2, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 2, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 2, # 'τ' - 12: 2, # 'υ' - 28: 1, # 'φ' - 23: 1, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 2, # 'ύ' - 27: 0, # 'ώ' - }, - 35: { # 'Π' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 2, # 'Λ' - 38: 1, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 0, # 'Σ' - 33: 1, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 1, # 'Χ' - 57: 2, # 'Ω' - 17: 2, # 'ά' - 18: 1, # 'έ' - 22: 1, # 'ή' - 15: 2, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 2, # 'ό' - 26: 0, # 'ύ' - 27: 3, # 'ώ' - }, - 48: { # 'Ρ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 1, # 'Γ' - 41: 1, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 2, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 0, # 'Σ' - 33: 1, # 'Τ' - 45: 1, # 'Υ' - 56: 0, # 'Φ' - 50: 1, # 'Χ' - 57: 1, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 2, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 1, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 2, # 'ύ' - 27: 0, # 'ώ' - }, - 37: { # 'Σ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 1, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 2, # 'Κ' - 53: 0, # 'Λ' - 38: 2, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 2, # 'Υ' - 56: 0, # 'Φ' - 50: 2, # 'Χ' - 57: 2, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 2, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 2, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 2, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 2, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 2, # 'ύ' - 27: 2, # 'ώ' - }, - 33: { # 'Τ' - 60: 0, # 'e' - 55: 1, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 2, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 2, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 0, # 'Σ' - 33: 1, # 'Τ' - 45: 1, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 2, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 0, # 'ή' - 15: 2, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 2, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 2, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 2, # 'ό' - 26: 2, # 'ύ' - 27: 3, # 'ώ' - }, - 45: { # 'Υ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 2, # 'Γ' - 41: 0, # 'Δ' - 34: 1, # 'Ε' - 40: 2, # 'Η' - 52: 2, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 1, # 'Λ' - 38: 2, # 'Μ' - 49: 2, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 2, # 'Π' - 48: 1, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 1, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 3, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 56: { # 'Φ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 1, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 1, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 2, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 2, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 1, # 'ύ' - 27: 1, # 'ώ' - }, - 50: { # 'Χ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 1, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 2, # 'Ε' - 40: 2, # 'Η' - 52: 0, # 'Θ' - 47: 2, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 1, # 'Ν' - 59: 0, # 'Ξ' - 39: 1, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 1, # 'Χ' - 57: 1, # 'Ω' - 17: 2, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 2, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 2, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 57: { # 'Ω' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 1, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 1, # 'Λ' - 38: 0, # 'Μ' - 49: 2, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 2, # 'Ρ' - 37: 2, # 'Σ' - 33: 2, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 2, # 'ρ' - 14: 2, # 'ς' - 7: 2, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 1, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 17: { # 'ά' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 3, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 3, # 'ε' - 32: 3, # 'ζ' - 13: 0, # 'η' - 25: 3, # 'θ' - 5: 2, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 0, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 3, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 18: { # 'έ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 3, # 'α' - 29: 2, # 'β' - 20: 3, # 'γ' - 21: 2, # 'δ' - 3: 3, # 'ε' - 32: 2, # 'ζ' - 13: 0, # 'η' - 25: 3, # 'θ' - 5: 0, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 3, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 22: { # 'ή' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 1, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 3, # 'θ' - 5: 0, # 'ι' - 11: 3, # 'κ' - 16: 2, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 0, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 15: { # 'ί' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 3, # 'α' - 29: 2, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 3, # 'ε' - 32: 3, # 'ζ' - 13: 3, # 'η' - 25: 3, # 'θ' - 5: 0, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 1, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 3, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 1: { # 'α' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 2, # 'έ' - 22: 0, # 'ή' - 15: 3, # 'ί' - 1: 0, # 'α' - 29: 3, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 2, # 'ε' - 32: 3, # 'ζ' - 13: 1, # 'η' - 25: 3, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 2, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 0, # 'ω' - 19: 2, # 'ό' - 26: 2, # 'ύ' - 27: 0, # 'ώ' - }, - 29: { # 'β' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 2, # 'έ' - 22: 3, # 'ή' - 15: 2, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 2, # 'γ' - 21: 2, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 3, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 2, # 'ό' - 26: 2, # 'ύ' - 27: 2, # 'ώ' - }, - 20: { # 'γ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 3, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 3, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 2, # 'ύ' - 27: 3, # 'ώ' - }, - 21: { # 'δ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 3: { # 'ε' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 3, # 'ί' - 1: 2, # 'α' - 29: 3, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 2, # 'ε' - 32: 2, # 'ζ' - 13: 0, # 'η' - 25: 3, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 2, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 3, # 'ω' - 19: 2, # 'ό' - 26: 3, # 'ύ' - 27: 2, # 'ώ' - }, - 32: { # 'ζ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 2, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 1, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 2, # 'ό' - 26: 0, # 'ύ' - 27: 2, # 'ώ' - }, - 13: { # 'η' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 3, # 'γ' - 21: 2, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 3, # 'θ' - 5: 0, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 0, # 'ο' - 9: 2, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 25: { # 'θ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 2, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 1, # 'λ' - 10: 3, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 5: { # 'ι' - 60: 0, # 'e' - 55: 1, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 1, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 0, # 'ί' - 1: 3, # 'α' - 29: 3, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 3, # 'ε' - 32: 2, # 'ζ' - 13: 3, # 'η' - 25: 3, # 'θ' - 5: 0, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 0, # 'ύ' - 27: 3, # 'ώ' - }, - 11: { # 'κ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 3, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 2, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 2, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 2, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 16: { # 'λ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 1, # 'β' - 20: 2, # 'γ' - 21: 1, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 2, # 'θ' - 5: 3, # 'ι' - 11: 2, # 'κ' - 16: 3, # 'λ' - 10: 2, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 2, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 10: { # 'μ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 1, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 3, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 2, # 'υ' - 28: 3, # 'φ' - 23: 0, # 'χ' - 42: 2, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 2, # 'ύ' - 27: 2, # 'ώ' - }, - 6: { # 'ν' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 3, # 'δ' - 3: 3, # 'ε' - 32: 2, # 'ζ' - 13: 3, # 'η' - 25: 3, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 1, # 'λ' - 10: 0, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 30: { # 'ξ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 2, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 2, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 2, # 'ό' - 26: 3, # 'ύ' - 27: 1, # 'ώ' - }, - 4: { # 'ο' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 2, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 2, # 'α' - 29: 3, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 3, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 2, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 2, # 'ω' - 19: 1, # 'ό' - 26: 3, # 'ύ' - 27: 2, # 'ώ' - }, - 9: { # 'π' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 3, # 'λ' - 10: 0, # 'μ' - 6: 2, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 2, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 2, # 'ύ' - 27: 3, # 'ώ' - }, - 8: { # 'ρ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 2, # 'β' - 20: 3, # 'γ' - 21: 2, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 3, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 1, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 3, # 'ο' - 9: 2, # 'π' - 8: 2, # 'ρ' - 14: 0, # 'ς' - 7: 2, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 14: { # 'ς' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 2, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 0, # 'θ' - 5: 0, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 0, # 'τ' - 12: 0, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 7: { # 'σ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 3, # 'β' - 20: 0, # 'γ' - 21: 2, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 3, # 'θ' - 5: 3, # 'ι' - 11: 3, # 'κ' - 16: 2, # 'λ' - 10: 3, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 3, # 'φ' - 23: 3, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 2, # 'ώ' - }, - 2: { # 'τ' - 60: 0, # 'e' - 55: 2, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 2, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 3, # 'ι' - 11: 2, # 'κ' - 16: 2, # 'λ' - 10: 3, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 2, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 12: { # 'υ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 3, # 'ή' - 15: 2, # 'ί' - 1: 3, # 'α' - 29: 2, # 'β' - 20: 3, # 'γ' - 21: 2, # 'δ' - 3: 2, # 'ε' - 32: 2, # 'ζ' - 13: 2, # 'η' - 25: 3, # 'θ' - 5: 2, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 3, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 2, # 'ω' - 19: 2, # 'ό' - 26: 0, # 'ύ' - 27: 2, # 'ώ' - }, - 28: { # 'φ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 3, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 2, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 0, # 'μ' - 6: 1, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 1, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 2, # 'ύ' - 27: 2, # 'ώ' - }, - 23: { # 'χ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 3, # 'ά' - 18: 2, # 'έ' - 22: 3, # 'ή' - 15: 3, # 'ί' - 1: 3, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 2, # 'θ' - 5: 3, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 2, # 'μ' - 6: 3, # 'ν' - 30: 0, # 'ξ' - 4: 3, # 'ο' - 9: 0, # 'π' - 8: 3, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 3, # 'τ' - 12: 3, # 'υ' - 28: 0, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 3, # 'ω' - 19: 3, # 'ό' - 26: 3, # 'ύ' - 27: 3, # 'ώ' - }, - 42: { # 'ψ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 2, # 'ά' - 18: 2, # 'έ' - 22: 1, # 'ή' - 15: 2, # 'ί' - 1: 2, # 'α' - 29: 0, # 'β' - 20: 0, # 'γ' - 21: 0, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 3, # 'η' - 25: 0, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 0, # 'λ' - 10: 0, # 'μ' - 6: 0, # 'ν' - 30: 0, # 'ξ' - 4: 2, # 'ο' - 9: 0, # 'π' - 8: 0, # 'ρ' - 14: 0, # 'ς' - 7: 0, # 'σ' - 2: 2, # 'τ' - 12: 1, # 'υ' - 28: 0, # 'φ' - 23: 0, # 'χ' - 42: 0, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 24: { # 'ω' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 1, # 'ά' - 18: 0, # 'έ' - 22: 2, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 2, # 'β' - 20: 3, # 'γ' - 21: 2, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 0, # 'η' - 25: 3, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 0, # 'ξ' - 4: 0, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 2, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 19: { # 'ό' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 3, # 'β' - 20: 3, # 'γ' - 21: 3, # 'δ' - 3: 1, # 'ε' - 32: 2, # 'ζ' - 13: 2, # 'η' - 25: 2, # 'θ' - 5: 2, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 1, # 'ξ' - 4: 2, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 3, # 'χ' - 42: 2, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 26: { # 'ύ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 2, # 'α' - 29: 2, # 'β' - 20: 2, # 'γ' - 21: 1, # 'δ' - 3: 3, # 'ε' - 32: 0, # 'ζ' - 13: 2, # 'η' - 25: 3, # 'θ' - 5: 0, # 'ι' - 11: 3, # 'κ' - 16: 3, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 2, # 'ξ' - 4: 3, # 'ο' - 9: 3, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 2, # 'φ' - 23: 2, # 'χ' - 42: 2, # 'ψ' - 24: 2, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, - 27: { # 'ώ' - 60: 0, # 'e' - 55: 0, # 'o' - 58: 0, # 't' - 36: 0, # '·' - 61: 0, # 'Ά' - 46: 0, # 'Έ' - 54: 0, # 'Ό' - 31: 0, # 'Α' - 51: 0, # 'Β' - 43: 0, # 'Γ' - 41: 0, # 'Δ' - 34: 0, # 'Ε' - 40: 0, # 'Η' - 52: 0, # 'Θ' - 47: 0, # 'Ι' - 44: 0, # 'Κ' - 53: 0, # 'Λ' - 38: 0, # 'Μ' - 49: 0, # 'Ν' - 59: 0, # 'Ξ' - 39: 0, # 'Ο' - 35: 0, # 'Π' - 48: 0, # 'Ρ' - 37: 0, # 'Σ' - 33: 0, # 'Τ' - 45: 0, # 'Υ' - 56: 0, # 'Φ' - 50: 0, # 'Χ' - 57: 0, # 'Ω' - 17: 0, # 'ά' - 18: 0, # 'έ' - 22: 0, # 'ή' - 15: 0, # 'ί' - 1: 0, # 'α' - 29: 1, # 'β' - 20: 0, # 'γ' - 21: 3, # 'δ' - 3: 0, # 'ε' - 32: 0, # 'ζ' - 13: 1, # 'η' - 25: 2, # 'θ' - 5: 2, # 'ι' - 11: 0, # 'κ' - 16: 2, # 'λ' - 10: 3, # 'μ' - 6: 3, # 'ν' - 30: 1, # 'ξ' - 4: 0, # 'ο' - 9: 2, # 'π' - 8: 3, # 'ρ' - 14: 3, # 'ς' - 7: 3, # 'σ' - 2: 3, # 'τ' - 12: 0, # 'υ' - 28: 1, # 'φ' - 23: 1, # 'χ' - 42: 0, # 'ψ' - 24: 0, # 'ω' - 19: 0, # 'ό' - 26: 0, # 'ύ' - 27: 0, # 'ώ' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -WINDOWS_1253_GREEK_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 82, # 'A' - 66: 100, # 'B' - 67: 104, # 'C' - 68: 94, # 'D' - 69: 98, # 'E' - 70: 101, # 'F' - 71: 116, # 'G' - 72: 102, # 'H' - 73: 111, # 'I' - 74: 187, # 'J' - 75: 117, # 'K' - 76: 92, # 'L' - 77: 88, # 'M' - 78: 113, # 'N' - 79: 85, # 'O' - 80: 79, # 'P' - 81: 118, # 'Q' - 82: 105, # 'R' - 83: 83, # 'S' - 84: 67, # 'T' - 85: 114, # 'U' - 86: 119, # 'V' - 87: 95, # 'W' - 88: 99, # 'X' - 89: 109, # 'Y' - 90: 188, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 72, # 'a' - 98: 70, # 'b' - 99: 80, # 'c' - 100: 81, # 'd' - 101: 60, # 'e' - 102: 96, # 'f' - 103: 93, # 'g' - 104: 89, # 'h' - 105: 68, # 'i' - 106: 120, # 'j' - 107: 97, # 'k' - 108: 77, # 'l' - 109: 86, # 'm' - 110: 69, # 'n' - 111: 55, # 'o' - 112: 78, # 'p' - 113: 115, # 'q' - 114: 65, # 'r' - 115: 66, # 's' - 116: 58, # 't' - 117: 76, # 'u' - 118: 106, # 'v' - 119: 103, # 'w' - 120: 87, # 'x' - 121: 107, # 'y' - 122: 112, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 255, # '€' - 129: 255, # None - 130: 255, # '‚' - 131: 255, # 'ƒ' - 132: 255, # '„' - 133: 255, # '…' - 134: 255, # '†' - 135: 255, # '‡' - 136: 255, # None - 137: 255, # '‰' - 138: 255, # None - 139: 255, # '‹' - 140: 255, # None - 141: 255, # None - 142: 255, # None - 143: 255, # None - 144: 255, # None - 145: 255, # '‘' - 146: 255, # '’' - 147: 255, # '“' - 148: 255, # '”' - 149: 255, # '•' - 150: 255, # '–' - 151: 255, # '—' - 152: 255, # None - 153: 255, # '™' - 154: 255, # None - 155: 255, # '›' - 156: 255, # None - 157: 255, # None - 158: 255, # None - 159: 255, # None - 160: 253, # '\xa0' - 161: 233, # '΅' - 162: 61, # 'Ά' - 163: 253, # '£' - 164: 253, # '¤' - 165: 253, # '¥' - 166: 253, # '¦' - 167: 253, # '§' - 168: 253, # '¨' - 169: 253, # '©' - 170: 253, # None - 171: 253, # '«' - 172: 253, # '¬' - 173: 74, # '\xad' - 174: 253, # '®' - 175: 253, # '―' - 176: 253, # '°' - 177: 253, # '±' - 178: 253, # '²' - 179: 253, # '³' - 180: 247, # '΄' - 181: 253, # 'µ' - 182: 253, # '¶' - 183: 36, # '·' - 184: 46, # 'Έ' - 185: 71, # 'Ή' - 186: 73, # 'Ί' - 187: 253, # '»' - 188: 54, # 'Ό' - 189: 253, # '½' - 190: 108, # 'Ύ' - 191: 123, # 'Ώ' - 192: 110, # 'ΐ' - 193: 31, # 'Α' - 194: 51, # 'Β' - 195: 43, # 'Γ' - 196: 41, # 'Δ' - 197: 34, # 'Ε' - 198: 91, # 'Ζ' - 199: 40, # 'Η' - 200: 52, # 'Θ' - 201: 47, # 'Ι' - 202: 44, # 'Κ' - 203: 53, # 'Λ' - 204: 38, # 'Μ' - 205: 49, # 'Ν' - 206: 59, # 'Ξ' - 207: 39, # 'Ο' - 208: 35, # 'Π' - 209: 48, # 'Ρ' - 210: 250, # None - 211: 37, # 'Σ' - 212: 33, # 'Τ' - 213: 45, # 'Υ' - 214: 56, # 'Φ' - 215: 50, # 'Χ' - 216: 84, # 'Ψ' - 217: 57, # 'Ω' - 218: 120, # 'Ϊ' - 219: 121, # 'Ϋ' - 220: 17, # 'ά' - 221: 18, # 'έ' - 222: 22, # 'ή' - 223: 15, # 'ί' - 224: 124, # 'ΰ' - 225: 1, # 'α' - 226: 29, # 'β' - 227: 20, # 'γ' - 228: 21, # 'δ' - 229: 3, # 'ε' - 230: 32, # 'ζ' - 231: 13, # 'η' - 232: 25, # 'θ' - 233: 5, # 'ι' - 234: 11, # 'κ' - 235: 16, # 'λ' - 236: 10, # 'μ' - 237: 6, # 'ν' - 238: 30, # 'ξ' - 239: 4, # 'ο' - 240: 9, # 'π' - 241: 8, # 'ρ' - 242: 14, # 'ς' - 243: 7, # 'σ' - 244: 2, # 'τ' - 245: 12, # 'υ' - 246: 28, # 'φ' - 247: 23, # 'χ' - 248: 42, # 'ψ' - 249: 24, # 'ω' - 250: 64, # 'ϊ' - 251: 75, # 'ϋ' - 252: 19, # 'ό' - 253: 26, # 'ύ' - 254: 27, # 'ώ' - 255: 253, # None -} - -WINDOWS_1253_GREEK_MODEL = SingleByteCharSetModel( - charset_name="windows-1253", - language="Greek", - char_to_order_map=WINDOWS_1253_GREEK_CHAR_TO_ORDER, - language_model=GREEK_LANG_MODEL, - typical_positive_ratio=0.982851, - keep_ascii_letters=False, - alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ", -) - -ISO_8859_7_GREEK_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 82, # 'A' - 66: 100, # 'B' - 67: 104, # 'C' - 68: 94, # 'D' - 69: 98, # 'E' - 70: 101, # 'F' - 71: 116, # 'G' - 72: 102, # 'H' - 73: 111, # 'I' - 74: 187, # 'J' - 75: 117, # 'K' - 76: 92, # 'L' - 77: 88, # 'M' - 78: 113, # 'N' - 79: 85, # 'O' - 80: 79, # 'P' - 81: 118, # 'Q' - 82: 105, # 'R' - 83: 83, # 'S' - 84: 67, # 'T' - 85: 114, # 'U' - 86: 119, # 'V' - 87: 95, # 'W' - 88: 99, # 'X' - 89: 109, # 'Y' - 90: 188, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 72, # 'a' - 98: 70, # 'b' - 99: 80, # 'c' - 100: 81, # 'd' - 101: 60, # 'e' - 102: 96, # 'f' - 103: 93, # 'g' - 104: 89, # 'h' - 105: 68, # 'i' - 106: 120, # 'j' - 107: 97, # 'k' - 108: 77, # 'l' - 109: 86, # 'm' - 110: 69, # 'n' - 111: 55, # 'o' - 112: 78, # 'p' - 113: 115, # 'q' - 114: 65, # 'r' - 115: 66, # 's' - 116: 58, # 't' - 117: 76, # 'u' - 118: 106, # 'v' - 119: 103, # 'w' - 120: 87, # 'x' - 121: 107, # 'y' - 122: 112, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 255, # '\x80' - 129: 255, # '\x81' - 130: 255, # '\x82' - 131: 255, # '\x83' - 132: 255, # '\x84' - 133: 255, # '\x85' - 134: 255, # '\x86' - 135: 255, # '\x87' - 136: 255, # '\x88' - 137: 255, # '\x89' - 138: 255, # '\x8a' - 139: 255, # '\x8b' - 140: 255, # '\x8c' - 141: 255, # '\x8d' - 142: 255, # '\x8e' - 143: 255, # '\x8f' - 144: 255, # '\x90' - 145: 255, # '\x91' - 146: 255, # '\x92' - 147: 255, # '\x93' - 148: 255, # '\x94' - 149: 255, # '\x95' - 150: 255, # '\x96' - 151: 255, # '\x97' - 152: 255, # '\x98' - 153: 255, # '\x99' - 154: 255, # '\x9a' - 155: 255, # '\x9b' - 156: 255, # '\x9c' - 157: 255, # '\x9d' - 158: 255, # '\x9e' - 159: 255, # '\x9f' - 160: 253, # '\xa0' - 161: 233, # '‘' - 162: 90, # '’' - 163: 253, # '£' - 164: 253, # '€' - 165: 253, # '₯' - 166: 253, # '¦' - 167: 253, # '§' - 168: 253, # '¨' - 169: 253, # '©' - 170: 253, # 'ͺ' - 171: 253, # '«' - 172: 253, # '¬' - 173: 74, # '\xad' - 174: 253, # None - 175: 253, # '―' - 176: 253, # '°' - 177: 253, # '±' - 178: 253, # '²' - 179: 253, # '³' - 180: 247, # '΄' - 181: 248, # '΅' - 182: 61, # 'Ά' - 183: 36, # '·' - 184: 46, # 'Έ' - 185: 71, # 'Ή' - 186: 73, # 'Ί' - 187: 253, # '»' - 188: 54, # 'Ό' - 189: 253, # '½' - 190: 108, # 'Ύ' - 191: 123, # 'Ώ' - 192: 110, # 'ΐ' - 193: 31, # 'Α' - 194: 51, # 'Β' - 195: 43, # 'Γ' - 196: 41, # 'Δ' - 197: 34, # 'Ε' - 198: 91, # 'Ζ' - 199: 40, # 'Η' - 200: 52, # 'Θ' - 201: 47, # 'Ι' - 202: 44, # 'Κ' - 203: 53, # 'Λ' - 204: 38, # 'Μ' - 205: 49, # 'Ν' - 206: 59, # 'Ξ' - 207: 39, # 'Ο' - 208: 35, # 'Π' - 209: 48, # 'Ρ' - 210: 250, # None - 211: 37, # 'Σ' - 212: 33, # 'Τ' - 213: 45, # 'Υ' - 214: 56, # 'Φ' - 215: 50, # 'Χ' - 216: 84, # 'Ψ' - 217: 57, # 'Ω' - 218: 120, # 'Ϊ' - 219: 121, # 'Ϋ' - 220: 17, # 'ά' - 221: 18, # 'έ' - 222: 22, # 'ή' - 223: 15, # 'ί' - 224: 124, # 'ΰ' - 225: 1, # 'α' - 226: 29, # 'β' - 227: 20, # 'γ' - 228: 21, # 'δ' - 229: 3, # 'ε' - 230: 32, # 'ζ' - 231: 13, # 'η' - 232: 25, # 'θ' - 233: 5, # 'ι' - 234: 11, # 'κ' - 235: 16, # 'λ' - 236: 10, # 'μ' - 237: 6, # 'ν' - 238: 30, # 'ξ' - 239: 4, # 'ο' - 240: 9, # 'π' - 241: 8, # 'ρ' - 242: 14, # 'ς' - 243: 7, # 'σ' - 244: 2, # 'τ' - 245: 12, # 'υ' - 246: 28, # 'φ' - 247: 23, # 'χ' - 248: 42, # 'ψ' - 249: 24, # 'ω' - 250: 64, # 'ϊ' - 251: 75, # 'ϋ' - 252: 19, # 'ό' - 253: 26, # 'ύ' - 254: 27, # 'ώ' - 255: 253, # None -} - -ISO_8859_7_GREEK_MODEL = SingleByteCharSetModel( - charset_name="ISO-8859-7", - language="Greek", - char_to_order_map=ISO_8859_7_GREEK_CHAR_TO_ORDER, - language_model=GREEK_LANG_MODEL, - typical_positive_ratio=0.982851, - keep_ascii_letters=False, - alphabet="ΆΈΉΊΌΎΏΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩάέήίαβγδεζηθικλμνξοπρςστυφχψωόύώ", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langhebrewmodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langhebrewmodel.py deleted file mode 100644 index 86b3c5e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langhebrewmodel.py +++ /dev/null @@ -1,4380 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -HEBREW_LANG_MODEL = { - 50: { # 'a' - 50: 0, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 2, # 'l' - 54: 2, # 'n' - 49: 0, # 'o' - 51: 2, # 'r' - 43: 1, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 1, # 'ק' - 7: 0, # 'ר' - 10: 1, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 60: { # 'c' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 0, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 0, # 'n' - 49: 1, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 61: { # 'd' - 50: 1, # 'a' - 60: 0, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 1, # 'n' - 49: 2, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 0, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 1, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 42: { # 'e' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 2, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 2, # 'l' - 54: 2, # 'n' - 49: 1, # 'o' - 51: 2, # 'r' - 43: 2, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 1, # '–' - 52: 2, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 53: { # 'i' - 50: 1, # 'a' - 60: 2, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 0, # 'i' - 56: 1, # 'l' - 54: 2, # 'n' - 49: 2, # 'o' - 51: 1, # 'r' - 43: 2, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 56: { # 'l' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 2, # 'e' - 53: 2, # 'i' - 56: 2, # 'l' - 54: 1, # 'n' - 49: 1, # 'o' - 51: 0, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 54: { # 'n' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 1, # 'n' - 49: 1, # 'o' - 51: 0, # 'r' - 43: 1, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 2, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 49: { # 'o' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 2, # 'n' - 49: 1, # 'o' - 51: 2, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 51: { # 'r' - 50: 2, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 2, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 1, # 'n' - 49: 2, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 2, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 43: { # 's' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 0, # 'd' - 42: 2, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 1, # 'n' - 49: 1, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 2, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 44: { # 't' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 0, # 'd' - 42: 2, # 'e' - 53: 2, # 'i' - 56: 1, # 'l' - 54: 0, # 'n' - 49: 1, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 1, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 2, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 63: { # 'u' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 1, # 'n' - 49: 0, # 'o' - 51: 1, # 'r' - 43: 2, # 's' - 44: 1, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 34: { # '\xa0' - 50: 1, # 'a' - 60: 0, # 'c' - 61: 1, # 'd' - 42: 0, # 'e' - 53: 1, # 'i' - 56: 0, # 'l' - 54: 1, # 'n' - 49: 1, # 'o' - 51: 0, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 0, # 'u' - 34: 2, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 1, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 2, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 2, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 1, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 55: { # '´' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 1, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 2, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 1, # 'ן' - 12: 1, # 'נ' - 19: 1, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 48: { # '¼' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 1, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 39: { # '½' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 57: { # '¾' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 30: { # 'ְ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 1, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 1, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 2, # 'ג' - 16: 2, # 'ד' - 3: 2, # 'ה' - 2: 2, # 'ו' - 24: 2, # 'ז' - 14: 2, # 'ח' - 22: 2, # 'ט' - 1: 2, # 'י' - 25: 2, # 'ך' - 15: 2, # 'כ' - 4: 2, # 'ל' - 11: 1, # 'ם' - 6: 2, # 'מ' - 23: 0, # 'ן' - 12: 2, # 'נ' - 19: 2, # 'ס' - 13: 2, # 'ע' - 26: 0, # 'ף' - 18: 2, # 'פ' - 27: 0, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 59: { # 'ֱ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 1, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 1, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 2, # 'ל' - 11: 0, # 'ם' - 6: 2, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 41: { # 'ֲ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 2, # 'ב' - 20: 1, # 'ג' - 16: 2, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 1, # 'י' - 25: 1, # 'ך' - 15: 1, # 'כ' - 4: 2, # 'ל' - 11: 0, # 'ם' - 6: 2, # 'מ' - 23: 0, # 'ן' - 12: 2, # 'נ' - 19: 1, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 2, # 'צ' - 17: 1, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 33: { # 'ִ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 1, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 1, # 'ִ' - 37: 0, # 'ֵ' - 36: 1, # 'ֶ' - 31: 0, # 'ַ' - 29: 1, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 1, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 2, # 'ב' - 20: 2, # 'ג' - 16: 2, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 2, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 2, # 'כ' - 4: 2, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 2, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 2, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 37: { # 'ֵ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 1, # 'ֶ' - 31: 1, # 'ַ' - 29: 1, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 1, # 'ג' - 16: 2, # 'ד' - 3: 2, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 2, # 'ח' - 22: 1, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 1, # 'כ' - 4: 2, # 'ל' - 11: 2, # 'ם' - 6: 1, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 1, # 'ס' - 13: 2, # 'ע' - 26: 1, # 'ף' - 18: 1, # 'פ' - 27: 1, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 36: { # 'ֶ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 1, # 'ֶ' - 31: 1, # 'ַ' - 29: 1, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 1, # 'ג' - 16: 2, # 'ד' - 3: 2, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 2, # 'ח' - 22: 1, # 'ט' - 1: 2, # 'י' - 25: 2, # 'ך' - 15: 1, # 'כ' - 4: 2, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 2, # 'ס' - 13: 1, # 'ע' - 26: 1, # 'ף' - 18: 1, # 'פ' - 27: 2, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 31: { # 'ַ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 1, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 1, # 'ֶ' - 31: 0, # 'ַ' - 29: 2, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 2, # 'ג' - 16: 2, # 'ד' - 3: 2, # 'ה' - 2: 1, # 'ו' - 24: 2, # 'ז' - 14: 2, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 2, # 'כ' - 4: 2, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 2, # 'ס' - 13: 2, # 'ע' - 26: 2, # 'ף' - 18: 2, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 29: { # 'ָ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 1, # 'ַ' - 29: 2, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 1, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 2, # 'ג' - 16: 2, # 'ד' - 3: 3, # 'ה' - 2: 2, # 'ו' - 24: 2, # 'ז' - 14: 2, # 'ח' - 22: 1, # 'ט' - 1: 2, # 'י' - 25: 2, # 'ך' - 15: 2, # 'כ' - 4: 2, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 1, # 'ס' - 13: 2, # 'ע' - 26: 1, # 'ף' - 18: 2, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 35: { # 'ֹ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 1, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 1, # 'ג' - 16: 2, # 'ד' - 3: 2, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 1, # 'י' - 25: 1, # 'ך' - 15: 2, # 'כ' - 4: 2, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 2, # 'ס' - 13: 2, # 'ע' - 26: 1, # 'ף' - 18: 2, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 62: { # 'ֻ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 1, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 2, # 'ל' - 11: 1, # 'ם' - 6: 1, # 'מ' - 23: 1, # 'ן' - 12: 1, # 'נ' - 19: 1, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 28: { # 'ּ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 3, # 'ְ' - 59: 0, # 'ֱ' - 41: 1, # 'ֲ' - 33: 3, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 3, # 'ַ' - 29: 3, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 0, # 'ּ' - 38: 2, # 'ׁ' - 45: 1, # 'ׂ' - 9: 2, # 'א' - 8: 2, # 'ב' - 20: 1, # 'ג' - 16: 2, # 'ד' - 3: 1, # 'ה' - 2: 2, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 2, # 'י' - 25: 2, # 'ך' - 15: 2, # 'כ' - 4: 2, # 'ל' - 11: 1, # 'ם' - 6: 2, # 'מ' - 23: 1, # 'ן' - 12: 2, # 'נ' - 19: 1, # 'ס' - 13: 2, # 'ע' - 26: 1, # 'ף' - 18: 1, # 'פ' - 27: 1, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 2, # 'ר' - 10: 2, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 38: { # 'ׁ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 2, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 45: { # 'ׂ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 1, # 'ֵ' - 36: 2, # 'ֶ' - 31: 1, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 1, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 2, # 'ו' - 24: 0, # 'ז' - 14: 1, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 1, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 0, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 0, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 9: { # 'א' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 2, # 'ֱ' - 41: 2, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 2, # 'ע' - 26: 3, # 'ף' - 18: 3, # 'פ' - 27: 1, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 8: { # 'ב' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 1, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 3, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 1, # 'ף' - 18: 3, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 1, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 20: { # 'ג' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 2, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 1, # 'ִ' - 37: 1, # 'ֵ' - 36: 1, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 0, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 3, # 'ב' - 20: 2, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 2, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 1, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 2, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 2, # 'פ' - 27: 1, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 16: { # 'ד' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 1, # 'ז' - 14: 2, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 2, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 2, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 0, # 'ץ' - 21: 2, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 3: { # 'ה' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 1, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 1, # 'ְ' - 59: 1, # 'ֱ' - 41: 2, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 3, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 0, # 'ף' - 18: 3, # 'פ' - 27: 1, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 1, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 2: { # 'ו' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 1, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 1, # 'ֵ' - 36: 1, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 3, # 'ֹ' - 62: 0, # 'ֻ' - 28: 3, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 3, # 'ף' - 18: 3, # 'פ' - 27: 3, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 1, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 24: { # 'ז' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 1, # 'ֲ' - 33: 1, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 2, # 'ב' - 20: 2, # 'ג' - 16: 2, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 2, # 'ז' - 14: 2, # 'ח' - 22: 1, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 2, # 'נ' - 19: 1, # 'ס' - 13: 2, # 'ע' - 26: 1, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 2, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 1, # 'ש' - 5: 2, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 14: { # 'ח' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 1, # 'ֱ' - 41: 2, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 3, # 'ב' - 20: 2, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 2, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 2, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 1, # 'ע' - 26: 2, # 'ף' - 18: 2, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 22: { # 'ט' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 1, # 'ֵ' - 36: 1, # 'ֶ' - 31: 2, # 'ַ' - 29: 1, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 1, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 1, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 2, # 'ז' - 14: 3, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 2, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 2, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 3, # 'ר' - 10: 2, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 1: { # 'י' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 1, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 3, # 'ף' - 18: 3, # 'פ' - 27: 3, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 1, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 25: { # 'ך' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 2, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 1, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 1, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 1, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 15: { # 'כ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 3, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 2, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 2, # 'ע' - 26: 3, # 'ף' - 18: 3, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 2, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 4: { # 'ל' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 3, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 1, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 11: { # 'ם' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 1, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 0, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 1, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 6: { # 'מ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 0, # 'ף' - 18: 3, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 23: { # 'ן' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 1, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 0, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 1, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 1, # 'ס' - 13: 1, # 'ע' - 26: 1, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 1, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 1, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 12: { # 'נ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 19: { # 'ס' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 1, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 1, # 'ָ' - 35: 1, # 'ֹ' - 62: 2, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 1, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 2, # 'ס' - 13: 3, # 'ע' - 26: 3, # 'ף' - 18: 3, # 'פ' - 27: 0, # 'ץ' - 21: 2, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 1, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 13: { # 'ע' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 1, # 'ְ' - 59: 1, # 'ֱ' - 41: 2, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 1, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 2, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 2, # 'ע' - 26: 1, # 'ף' - 18: 2, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 26: { # 'ף' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 1, # 'ו' - 24: 0, # 'ז' - 14: 1, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 1, # 'ס' - 13: 0, # 'ע' - 26: 1, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 1, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 18: { # 'פ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 1, # 'ֵ' - 36: 2, # 'ֶ' - 31: 1, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 2, # 'ב' - 20: 3, # 'ג' - 16: 2, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 2, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 2, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 2, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 27: { # 'ץ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 1, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 0, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 21: { # 'צ' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 1, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 2, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 1, # 'ז' - 14: 3, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 1, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 1, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 2, # 'ץ' - 21: 2, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 0, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 17: { # 'ק' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 1, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 1, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 2, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 2, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 1, # 'ך' - 15: 1, # 'כ' - 4: 3, # 'ל' - 11: 2, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 2, # 'ץ' - 21: 3, # 'צ' - 17: 2, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 7: { # 'ר' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 2, # '´' - 48: 1, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 1, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 2, # 'ֹ' - 62: 1, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 3, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 3, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 3, # 'ץ' - 21: 3, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 10: { # 'ש' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 1, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 1, # 'ִ' - 37: 1, # 'ֵ' - 36: 1, # 'ֶ' - 31: 1, # 'ַ' - 29: 1, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 3, # 'ׁ' - 45: 2, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 3, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 2, # 'ז' - 14: 3, # 'ח' - 22: 3, # 'ט' - 1: 3, # 'י' - 25: 3, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 2, # 'ן' - 12: 3, # 'נ' - 19: 2, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 1, # '…' - }, - 5: { # 'ת' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 1, # '\xa0' - 55: 0, # '´' - 48: 1, # '¼' - 39: 1, # '½' - 57: 0, # '¾' - 30: 2, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 2, # 'ִ' - 37: 2, # 'ֵ' - 36: 2, # 'ֶ' - 31: 2, # 'ַ' - 29: 2, # 'ָ' - 35: 1, # 'ֹ' - 62: 1, # 'ֻ' - 28: 2, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 3, # 'א' - 8: 3, # 'ב' - 20: 3, # 'ג' - 16: 2, # 'ד' - 3: 3, # 'ה' - 2: 3, # 'ו' - 24: 2, # 'ז' - 14: 3, # 'ח' - 22: 2, # 'ט' - 1: 3, # 'י' - 25: 2, # 'ך' - 15: 3, # 'כ' - 4: 3, # 'ל' - 11: 3, # 'ם' - 6: 3, # 'מ' - 23: 3, # 'ן' - 12: 3, # 'נ' - 19: 2, # 'ס' - 13: 3, # 'ע' - 26: 2, # 'ף' - 18: 3, # 'פ' - 27: 1, # 'ץ' - 21: 2, # 'צ' - 17: 3, # 'ק' - 7: 3, # 'ר' - 10: 3, # 'ש' - 5: 3, # 'ת' - 32: 1, # '–' - 52: 1, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, - 32: { # '–' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 1, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 1, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 0, # 'ז' - 14: 1, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 1, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 1, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 52: { # '’' - 50: 1, # 'a' - 60: 0, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 1, # 'r' - 43: 2, # 's' - 44: 2, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 1, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 47: { # '“' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 1, # 'l' - 54: 1, # 'n' - 49: 1, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 1, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 2, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 1, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 1, # 'ח' - 22: 1, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 1, # 'ס' - 13: 1, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 1, # 'צ' - 17: 1, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 46: { # '”' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 1, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 1, # 'ב' - 20: 1, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 1, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 0, # '†' - 40: 0, # '…' - }, - 58: { # '†' - 50: 0, # 'a' - 60: 0, # 'c' - 61: 0, # 'd' - 42: 0, # 'e' - 53: 0, # 'i' - 56: 0, # 'l' - 54: 0, # 'n' - 49: 0, # 'o' - 51: 0, # 'r' - 43: 0, # 's' - 44: 0, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 0, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 0, # 'ה' - 2: 0, # 'ו' - 24: 0, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 0, # 'י' - 25: 0, # 'ך' - 15: 0, # 'כ' - 4: 0, # 'ל' - 11: 0, # 'ם' - 6: 0, # 'מ' - 23: 0, # 'ן' - 12: 0, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 0, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 0, # 'ר' - 10: 0, # 'ש' - 5: 0, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 0, # '”' - 58: 2, # '†' - 40: 0, # '…' - }, - 40: { # '…' - 50: 1, # 'a' - 60: 1, # 'c' - 61: 1, # 'd' - 42: 1, # 'e' - 53: 1, # 'i' - 56: 0, # 'l' - 54: 1, # 'n' - 49: 0, # 'o' - 51: 1, # 'r' - 43: 1, # 's' - 44: 1, # 't' - 63: 0, # 'u' - 34: 0, # '\xa0' - 55: 0, # '´' - 48: 0, # '¼' - 39: 0, # '½' - 57: 0, # '¾' - 30: 0, # 'ְ' - 59: 0, # 'ֱ' - 41: 0, # 'ֲ' - 33: 0, # 'ִ' - 37: 0, # 'ֵ' - 36: 0, # 'ֶ' - 31: 0, # 'ַ' - 29: 0, # 'ָ' - 35: 0, # 'ֹ' - 62: 0, # 'ֻ' - 28: 0, # 'ּ' - 38: 0, # 'ׁ' - 45: 0, # 'ׂ' - 9: 1, # 'א' - 8: 0, # 'ב' - 20: 0, # 'ג' - 16: 0, # 'ד' - 3: 1, # 'ה' - 2: 1, # 'ו' - 24: 1, # 'ז' - 14: 0, # 'ח' - 22: 0, # 'ט' - 1: 1, # 'י' - 25: 0, # 'ך' - 15: 1, # 'כ' - 4: 1, # 'ל' - 11: 0, # 'ם' - 6: 1, # 'מ' - 23: 0, # 'ן' - 12: 1, # 'נ' - 19: 0, # 'ס' - 13: 0, # 'ע' - 26: 0, # 'ף' - 18: 1, # 'פ' - 27: 0, # 'ץ' - 21: 0, # 'צ' - 17: 0, # 'ק' - 7: 1, # 'ר' - 10: 1, # 'ש' - 5: 1, # 'ת' - 32: 0, # '–' - 52: 0, # '’' - 47: 0, # '“' - 46: 1, # '”' - 58: 0, # '†' - 40: 2, # '…' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -WINDOWS_1255_HEBREW_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 69, # 'A' - 66: 91, # 'B' - 67: 79, # 'C' - 68: 80, # 'D' - 69: 92, # 'E' - 70: 89, # 'F' - 71: 97, # 'G' - 72: 90, # 'H' - 73: 68, # 'I' - 74: 111, # 'J' - 75: 112, # 'K' - 76: 82, # 'L' - 77: 73, # 'M' - 78: 95, # 'N' - 79: 85, # 'O' - 80: 78, # 'P' - 81: 121, # 'Q' - 82: 86, # 'R' - 83: 71, # 'S' - 84: 67, # 'T' - 85: 102, # 'U' - 86: 107, # 'V' - 87: 84, # 'W' - 88: 114, # 'X' - 89: 103, # 'Y' - 90: 115, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 50, # 'a' - 98: 74, # 'b' - 99: 60, # 'c' - 100: 61, # 'd' - 101: 42, # 'e' - 102: 76, # 'f' - 103: 70, # 'g' - 104: 64, # 'h' - 105: 53, # 'i' - 106: 105, # 'j' - 107: 93, # 'k' - 108: 56, # 'l' - 109: 65, # 'm' - 110: 54, # 'n' - 111: 49, # 'o' - 112: 66, # 'p' - 113: 110, # 'q' - 114: 51, # 'r' - 115: 43, # 's' - 116: 44, # 't' - 117: 63, # 'u' - 118: 81, # 'v' - 119: 77, # 'w' - 120: 98, # 'x' - 121: 75, # 'y' - 122: 108, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 124, # '€' - 129: 202, # None - 130: 203, # '‚' - 131: 204, # 'ƒ' - 132: 205, # '„' - 133: 40, # '…' - 134: 58, # '†' - 135: 206, # '‡' - 136: 207, # 'ˆ' - 137: 208, # '‰' - 138: 209, # None - 139: 210, # '‹' - 140: 211, # None - 141: 212, # None - 142: 213, # None - 143: 214, # None - 144: 215, # None - 145: 83, # '‘' - 146: 52, # '’' - 147: 47, # '“' - 148: 46, # '”' - 149: 72, # '•' - 150: 32, # '–' - 151: 94, # '—' - 152: 216, # '˜' - 153: 113, # '™' - 154: 217, # None - 155: 109, # '›' - 156: 218, # None - 157: 219, # None - 158: 220, # None - 159: 221, # None - 160: 34, # '\xa0' - 161: 116, # '¡' - 162: 222, # '¢' - 163: 118, # '£' - 164: 100, # '₪' - 165: 223, # '¥' - 166: 224, # '¦' - 167: 117, # '§' - 168: 119, # '¨' - 169: 104, # '©' - 170: 125, # '×' - 171: 225, # '«' - 172: 226, # '¬' - 173: 87, # '\xad' - 174: 99, # '®' - 175: 227, # '¯' - 176: 106, # '°' - 177: 122, # '±' - 178: 123, # '²' - 179: 228, # '³' - 180: 55, # '´' - 181: 229, # 'µ' - 182: 230, # '¶' - 183: 101, # '·' - 184: 231, # '¸' - 185: 232, # '¹' - 186: 120, # '÷' - 187: 233, # '»' - 188: 48, # '¼' - 189: 39, # '½' - 190: 57, # '¾' - 191: 234, # '¿' - 192: 30, # 'ְ' - 193: 59, # 'ֱ' - 194: 41, # 'ֲ' - 195: 88, # 'ֳ' - 196: 33, # 'ִ' - 197: 37, # 'ֵ' - 198: 36, # 'ֶ' - 199: 31, # 'ַ' - 200: 29, # 'ָ' - 201: 35, # 'ֹ' - 202: 235, # None - 203: 62, # 'ֻ' - 204: 28, # 'ּ' - 205: 236, # 'ֽ' - 206: 126, # '־' - 207: 237, # 'ֿ' - 208: 238, # '׀' - 209: 38, # 'ׁ' - 210: 45, # 'ׂ' - 211: 239, # '׃' - 212: 240, # 'װ' - 213: 241, # 'ױ' - 214: 242, # 'ײ' - 215: 243, # '׳' - 216: 127, # '״' - 217: 244, # None - 218: 245, # None - 219: 246, # None - 220: 247, # None - 221: 248, # None - 222: 249, # None - 223: 250, # None - 224: 9, # 'א' - 225: 8, # 'ב' - 226: 20, # 'ג' - 227: 16, # 'ד' - 228: 3, # 'ה' - 229: 2, # 'ו' - 230: 24, # 'ז' - 231: 14, # 'ח' - 232: 22, # 'ט' - 233: 1, # 'י' - 234: 25, # 'ך' - 235: 15, # 'כ' - 236: 4, # 'ל' - 237: 11, # 'ם' - 238: 6, # 'מ' - 239: 23, # 'ן' - 240: 12, # 'נ' - 241: 19, # 'ס' - 242: 13, # 'ע' - 243: 26, # 'ף' - 244: 18, # 'פ' - 245: 27, # 'ץ' - 246: 21, # 'צ' - 247: 17, # 'ק' - 248: 7, # 'ר' - 249: 10, # 'ש' - 250: 5, # 'ת' - 251: 251, # None - 252: 252, # None - 253: 128, # '\u200e' - 254: 96, # '\u200f' - 255: 253, # None -} - -WINDOWS_1255_HEBREW_MODEL = SingleByteCharSetModel( - charset_name="windows-1255", - language="Hebrew", - char_to_order_map=WINDOWS_1255_HEBREW_CHAR_TO_ORDER, - language_model=HEBREW_LANG_MODEL, - typical_positive_ratio=0.984004, - keep_ascii_letters=False, - alphabet="אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langhungarianmodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langhungarianmodel.py deleted file mode 100644 index bd6630a..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langhungarianmodel.py +++ /dev/null @@ -1,4649 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -HUNGARIAN_LANG_MODEL = { - 28: { # 'A' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 2, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 2, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 2, # 'K' - 41: 2, # 'L' - 34: 1, # 'M' - 35: 2, # 'N' - 47: 1, # 'O' - 46: 2, # 'P' - 43: 2, # 'R' - 33: 2, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 2, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 1, # 'j' - 7: 2, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 2, # 'n' - 8: 0, # 'o' - 23: 2, # 'p' - 10: 2, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 1, # 'u' - 19: 1, # 'v' - 62: 1, # 'x' - 16: 0, # 'y' - 11: 3, # 'z' - 51: 1, # 'Á' - 44: 0, # 'É' - 61: 1, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 40: { # 'B' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 0, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 3, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 2, # 'i' - 22: 1, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 3, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 54: { # 'C' - 28: 1, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 0, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 2, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 0, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 1, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 3, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 1, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 45: { # 'D' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 0, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 0, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 3, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 1, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 1, # 'o' - 23: 0, # 'p' - 10: 2, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 2, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 1, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 0, # 'ű' - }, - 32: { # 'E' - 28: 1, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 2, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 2, # 'K' - 41: 2, # 'L' - 34: 2, # 'M' - 35: 2, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 2, # 'R' - 33: 2, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 1, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 3, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 2, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 2, # 's' - 3: 1, # 't' - 21: 2, # 'u' - 19: 1, # 'v' - 62: 1, # 'x' - 16: 0, # 'y' - 11: 3, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 0, # 'Ú' - 63: 1, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 1, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 50: { # 'F' - 28: 1, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 0, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 0, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 0, # 'V' - 55: 1, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 1, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 2, # 'i' - 22: 1, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 2, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 0, # 'Ú' - 63: 1, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 2, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 49: { # 'G' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 2, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 1, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 2, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 2, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 0, # 'ű' - }, - 38: { # 'H' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 0, # 'D' - 32: 1, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 1, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 1, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 1, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 0, # 'V' - 55: 1, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 2, # 'i' - 22: 1, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 0, # 'n' - 8: 3, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 2, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 2, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 1, # 'é' - 30: 2, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 39: { # 'I' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 2, # 'K' - 41: 2, # 'L' - 34: 1, # 'M' - 35: 2, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 2, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 2, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 0, # 'e' - 27: 1, # 'f' - 12: 2, # 'g' - 20: 1, # 'h' - 9: 0, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 1, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 2, # 's' - 3: 2, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 53: { # 'J' - 28: 2, # 'A' - 40: 0, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 1, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 1, # 'o' - 23: 0, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 2, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 2, # 'ó' - 24: 2, # 'ö' - 31: 1, # 'ú' - 29: 0, # 'ü' - 42: 1, # 'ő' - 56: 0, # 'ű' - }, - 36: { # 'K' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 0, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 1, # 'f' - 12: 0, # 'g' - 20: 1, # 'h' - 9: 3, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 2, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 2, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 2, # 'ö' - 31: 1, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 0, # 'ű' - }, - 41: { # 'L' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 2, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 3, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 2, # 'i' - 22: 1, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 2, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 0, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 34: { # 'M' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 0, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 3, # 'a' - 18: 0, # 'b' - 26: 1, # 'c' - 17: 0, # 'd' - 1: 3, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 3, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 3, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 2, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 1, # 'ű' - }, - 35: { # 'N' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 2, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 2, # 'Y' - 52: 1, # 'Z' - 2: 3, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 3, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 2, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 2, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 1, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 1, # 'ő' - 56: 0, # 'ű' - }, - 47: { # 'O' - 28: 1, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 2, # 'K' - 41: 2, # 'L' - 34: 2, # 'M' - 35: 2, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 2, # 'R' - 33: 2, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 1, # 'j' - 7: 2, # 'k' - 6: 2, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 1, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 1, # 's' - 3: 2, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 1, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 1, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 46: { # 'P' - 28: 1, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 0, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 2, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 1, # 'f' - 12: 0, # 'g' - 20: 1, # 'h' - 9: 2, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 2, # 'r' - 5: 1, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 0, # 'Ú' - 63: 1, # 'Ü' - 14: 3, # 'á' - 15: 2, # 'é' - 30: 0, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 0, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 0, # 'ű' - }, - 43: { # 'R' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 2, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 1, # 'h' - 9: 2, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 2, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 2, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 33: { # 'S' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 2, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 3, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 1, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 1, # 'h' - 9: 2, # 'i' - 22: 0, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 1, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 1, # 't' - 21: 1, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 3, # 'z' - 51: 2, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 37: { # 'T' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 1, # 'P' - 43: 2, # 'R' - 33: 1, # 'S' - 37: 2, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 2, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 1, # 'h' - 9: 2, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 0, # 't' - 21: 2, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 1, # 'z' - 51: 2, # 'Á' - 44: 2, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 2, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 57: { # 'U' - 28: 1, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 2, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 1, # 'e' - 27: 0, # 'f' - 12: 2, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 1, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 48: { # 'V' - 28: 2, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 0, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 2, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 2, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 2, # 'o' - 23: 0, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 2, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 0, # 'Ú' - 63: 1, # 'Ü' - 14: 2, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 0, # 'ó' - 24: 1, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 55: { # 'Y' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 1, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 2, # 'Z' - 2: 1, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 1, # 'd' - 1: 1, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 8: 1, # 'o' - 23: 1, # 'p' - 10: 0, # 'r' - 5: 0, # 's' - 3: 0, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 1, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 52: { # 'Z' - 28: 2, # 'A' - 40: 1, # 'B' - 54: 0, # 'C' - 45: 1, # 'D' - 32: 2, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 2, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 2, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 2, # 'S' - 37: 1, # 'T' - 57: 1, # 'U' - 48: 1, # 'V' - 55: 1, # 'Y' - 52: 1, # 'Z' - 2: 1, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 1, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 1, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 8: 1, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 2, # 's' - 3: 0, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 2, # 'Á' - 44: 1, # 'É' - 61: 1, # 'Í' - 58: 1, # 'Ó' - 59: 1, # 'Ö' - 60: 1, # 'Ú' - 63: 1, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 2: { # 'a' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 2, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 2, # 'o' - 23: 3, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 1, # 'x' - 16: 2, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 18: { # 'b' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 2, # 'k' - 6: 2, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 2, # 's' - 3: 1, # 't' - 21: 3, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 3, # 'ó' - 24: 2, # 'ö' - 31: 2, # 'ú' - 29: 2, # 'ü' - 42: 2, # 'ő' - 56: 1, # 'ű' - }, - 26: { # 'c' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 1, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 1, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 1, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 1, # 'j' - 7: 2, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 3, # 's' - 3: 2, # 't' - 21: 2, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 2, # 'á' - 15: 2, # 'é' - 30: 2, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 17: { # 'd' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 2, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 2, # 'k' - 6: 1, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 2, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 3, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 2, # 'ú' - 29: 2, # 'ü' - 42: 2, # 'ő' - 56: 1, # 'ű' - }, - 1: { # 'e' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 2, # 'e' - 27: 3, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 2, # 'o' - 23: 3, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 2, # 'u' - 19: 3, # 'v' - 62: 2, # 'x' - 16: 2, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 27: { # 'f' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 3, # 'o' - 23: 0, # 'p' - 10: 3, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 2, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 0, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 3, # 'ö' - 31: 1, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 12: { # 'g' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 2, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 2, # 'k' - 6: 3, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 3, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 3, # 'ó' - 24: 2, # 'ö' - 31: 2, # 'ú' - 29: 2, # 'ü' - 42: 2, # 'ő' - 56: 1, # 'ű' - }, - 20: { # 'h' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 0, # 'd' - 1: 3, # 'e' - 27: 0, # 'f' - 12: 1, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 3, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 2, # 's' - 3: 1, # 't' - 21: 3, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 2, # 'y' - 11: 0, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 3, # 'í' - 25: 2, # 'ó' - 24: 2, # 'ö' - 31: 2, # 'ú' - 29: 1, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 9: { # 'i' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 3, # 'e' - 27: 3, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 2, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 2, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 1, # 'x' - 16: 1, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 3, # 'ó' - 24: 1, # 'ö' - 31: 2, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 1, # 'ű' - }, - 22: { # 'j' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 2, # 'b' - 26: 1, # 'c' - 17: 3, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 2, # 'h' - 9: 1, # 'i' - 22: 2, # 'j' - 7: 2, # 'k' - 6: 2, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 2, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 1, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 3, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 7: { # 'k' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 1, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 2, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 3, # 'í' - 25: 2, # 'ó' - 24: 3, # 'ö' - 31: 1, # 'ú' - 29: 3, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 6: { # 'l' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 1, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 1, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 2, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 3, # 'e' - 27: 3, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 2, # 'p' - 10: 2, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 3, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 3, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 2, # 'ú' - 29: 2, # 'ü' - 42: 3, # 'ő' - 56: 1, # 'ű' - }, - 13: { # 'm' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 1, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 8: 3, # 'o' - 23: 3, # 'p' - 10: 2, # 'r' - 5: 2, # 's' - 3: 2, # 't' - 21: 3, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 2, # 'ó' - 24: 2, # 'ö' - 31: 2, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 2, # 'ű' - }, - 4: { # 'n' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 2, # 'p' - 10: 2, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 2, # 'v' - 62: 1, # 'x' - 16: 3, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 2, # 'ó' - 24: 3, # 'ö' - 31: 2, # 'ú' - 29: 3, # 'ü' - 42: 2, # 'ő' - 56: 1, # 'ű' - }, - 8: { # 'o' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 1, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 2, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 2, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 1, # 'o' - 23: 3, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 2, # 'u' - 19: 3, # 'v' - 62: 1, # 'x' - 16: 1, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 23: { # 'p' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 1, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 2, # 'k' - 6: 3, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 8: 3, # 'o' - 23: 3, # 'p' - 10: 3, # 'r' - 5: 2, # 's' - 3: 2, # 't' - 21: 3, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 2, # 'ó' - 24: 2, # 'ö' - 31: 1, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 10: { # 'r' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 1, # 'x' - 16: 2, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 3, # 'ú' - 29: 3, # 'ü' - 42: 2, # 'ő' - 56: 2, # 'ű' - }, - 5: { # 's' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 2, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 2, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 1, # 'j' - 7: 3, # 'k' - 6: 2, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 3, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 3, # 'ú' - 29: 3, # 'ü' - 42: 2, # 'ő' - 56: 1, # 'ű' - }, - 3: { # 't' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 1, # 'g' - 20: 3, # 'h' - 9: 3, # 'i' - 22: 3, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 3, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 3, # 'ú' - 29: 3, # 'ü' - 42: 3, # 'ő' - 56: 2, # 'ű' - }, - 21: { # 'u' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 2, # 'b' - 26: 2, # 'c' - 17: 3, # 'd' - 1: 2, # 'e' - 27: 1, # 'f' - 12: 3, # 'g' - 20: 2, # 'h' - 9: 2, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 1, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 1, # 'u' - 19: 3, # 'v' - 62: 1, # 'x' - 16: 1, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 2, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 0, # 'ö' - 31: 1, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 19: { # 'v' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 2, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 3, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 1, # 'r' - 5: 2, # 's' - 3: 2, # 't' - 21: 2, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 2, # 'ó' - 24: 2, # 'ö' - 31: 1, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 1, # 'ű' - }, - 62: { # 'x' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 0, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 1, # 'i' - 22: 0, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 1, # 'o' - 23: 1, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 1, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 1, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 16: { # 'y' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 2, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 3, # 'e' - 27: 2, # 'f' - 12: 2, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 2, # 'j' - 7: 2, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 2, # 'p' - 10: 2, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 2, # 'í' - 25: 2, # 'ó' - 24: 3, # 'ö' - 31: 2, # 'ú' - 29: 2, # 'ü' - 42: 1, # 'ő' - 56: 2, # 'ű' - }, - 11: { # 'z' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 3, # 'a' - 18: 2, # 'b' - 26: 1, # 'c' - 17: 3, # 'd' - 1: 3, # 'e' - 27: 1, # 'f' - 12: 2, # 'g' - 20: 2, # 'h' - 9: 3, # 'i' - 22: 1, # 'j' - 7: 3, # 'k' - 6: 2, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 3, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 3, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 3, # 'á' - 15: 3, # 'é' - 30: 3, # 'í' - 25: 3, # 'ó' - 24: 3, # 'ö' - 31: 2, # 'ú' - 29: 3, # 'ü' - 42: 2, # 'ő' - 56: 1, # 'ű' - }, - 51: { # 'Á' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 0, # 'E' - 50: 1, # 'F' - 49: 2, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 2, # 'L' - 34: 1, # 'M' - 35: 2, # 'N' - 47: 0, # 'O' - 46: 1, # 'P' - 43: 2, # 'R' - 33: 2, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 0, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 1, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 44: { # 'É' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 1, # 'E' - 50: 0, # 'F' - 49: 2, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 2, # 'L' - 34: 1, # 'M' - 35: 2, # 'N' - 47: 0, # 'O' - 46: 1, # 'P' - 43: 2, # 'R' - 33: 2, # 'S' - 37: 2, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 0, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 2, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 3, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 0, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 61: { # 'Í' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 0, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 1, # 'J' - 36: 0, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 0, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 2, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 1, # 'm' - 4: 0, # 'n' - 8: 0, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 0, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 58: { # 'Ó' - 28: 1, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 0, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 1, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 2, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 0, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 2, # 'h' - 9: 0, # 'i' - 22: 0, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 0, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 1, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 59: { # 'Ö' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 0, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 0, # 'O' - 46: 1, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 0, # 'b' - 26: 1, # 'c' - 17: 1, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 0, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 0, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 8: 0, # 'o' - 23: 0, # 'p' - 10: 2, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 60: { # 'Ú' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 1, # 'C' - 45: 1, # 'D' - 32: 0, # 'E' - 50: 1, # 'F' - 49: 1, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 0, # 'b' - 26: 0, # 'c' - 17: 0, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 2, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 2, # 'j' - 7: 0, # 'k' - 6: 0, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 8: 0, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 0, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 0, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 63: { # 'Ü' - 28: 0, # 'A' - 40: 1, # 'B' - 54: 0, # 'C' - 45: 1, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 1, # 'G' - 38: 1, # 'H' - 39: 0, # 'I' - 53: 1, # 'J' - 36: 1, # 'K' - 41: 1, # 'L' - 34: 1, # 'M' - 35: 1, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 1, # 'R' - 33: 1, # 'S' - 37: 1, # 'T' - 57: 0, # 'U' - 48: 1, # 'V' - 55: 0, # 'Y' - 52: 1, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 0, # 'c' - 17: 1, # 'd' - 1: 0, # 'e' - 27: 0, # 'f' - 12: 1, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 0, # 'j' - 7: 0, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 8: 0, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 1, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 14: { # 'á' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 3, # 'b' - 26: 3, # 'c' - 17: 3, # 'd' - 1: 1, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 2, # 'h' - 9: 2, # 'i' - 22: 3, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 1, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 2, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 1, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 2, # 'é' - 30: 1, # 'í' - 25: 0, # 'ó' - 24: 1, # 'ö' - 31: 0, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 15: { # 'é' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 3, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 3, # 'g' - 20: 3, # 'h' - 9: 2, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 1, # 'o' - 23: 3, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 0, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 30: { # 'í' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 0, # 'a' - 18: 1, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 0, # 'e' - 27: 1, # 'f' - 12: 3, # 'g' - 20: 0, # 'h' - 9: 0, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 2, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 2, # 's' - 3: 3, # 't' - 21: 0, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 25: { # 'ó' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 2, # 'a' - 18: 3, # 'b' - 26: 2, # 'c' - 17: 3, # 'd' - 1: 1, # 'e' - 27: 2, # 'f' - 12: 2, # 'g' - 20: 2, # 'h' - 9: 2, # 'i' - 22: 2, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 8: 1, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 1, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 0, # 'ó' - 24: 1, # 'ö' - 31: 1, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 24: { # 'ö' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 0, # 'a' - 18: 3, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 0, # 'e' - 27: 1, # 'f' - 12: 2, # 'g' - 20: 1, # 'h' - 9: 0, # 'i' - 22: 1, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 8: 0, # 'o' - 23: 2, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 3, # 't' - 21: 0, # 'u' - 19: 3, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 3, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 31: { # 'ú' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 1, # 'b' - 26: 2, # 'c' - 17: 1, # 'd' - 1: 1, # 'e' - 27: 2, # 'f' - 12: 3, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 3, # 'j' - 7: 1, # 'k' - 6: 3, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 3, # 'r' - 5: 3, # 's' - 3: 2, # 't' - 21: 1, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 1, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 29: { # 'ü' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 1, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 3, # 'g' - 20: 2, # 'h' - 9: 1, # 'i' - 22: 1, # 'j' - 7: 3, # 'k' - 6: 3, # 'l' - 13: 1, # 'm' - 4: 3, # 'n' - 8: 0, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 2, # 's' - 3: 2, # 't' - 21: 0, # 'u' - 19: 2, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 1, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 42: { # 'ő' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 2, # 'b' - 26: 1, # 'c' - 17: 2, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 1, # 'j' - 7: 2, # 'k' - 6: 3, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 8: 1, # 'o' - 23: 1, # 'p' - 10: 2, # 'r' - 5: 2, # 's' - 3: 2, # 't' - 21: 1, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 1, # 'é' - 30: 1, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 1, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, - 56: { # 'ű' - 28: 0, # 'A' - 40: 0, # 'B' - 54: 0, # 'C' - 45: 0, # 'D' - 32: 0, # 'E' - 50: 0, # 'F' - 49: 0, # 'G' - 38: 0, # 'H' - 39: 0, # 'I' - 53: 0, # 'J' - 36: 0, # 'K' - 41: 0, # 'L' - 34: 0, # 'M' - 35: 0, # 'N' - 47: 0, # 'O' - 46: 0, # 'P' - 43: 0, # 'R' - 33: 0, # 'S' - 37: 0, # 'T' - 57: 0, # 'U' - 48: 0, # 'V' - 55: 0, # 'Y' - 52: 0, # 'Z' - 2: 1, # 'a' - 18: 1, # 'b' - 26: 0, # 'c' - 17: 1, # 'd' - 1: 1, # 'e' - 27: 1, # 'f' - 12: 1, # 'g' - 20: 1, # 'h' - 9: 1, # 'i' - 22: 1, # 'j' - 7: 1, # 'k' - 6: 1, # 'l' - 13: 0, # 'm' - 4: 2, # 'n' - 8: 0, # 'o' - 23: 0, # 'p' - 10: 1, # 'r' - 5: 1, # 's' - 3: 1, # 't' - 21: 0, # 'u' - 19: 1, # 'v' - 62: 0, # 'x' - 16: 0, # 'y' - 11: 2, # 'z' - 51: 0, # 'Á' - 44: 0, # 'É' - 61: 0, # 'Í' - 58: 0, # 'Ó' - 59: 0, # 'Ö' - 60: 0, # 'Ú' - 63: 0, # 'Ü' - 14: 0, # 'á' - 15: 0, # 'é' - 30: 0, # 'í' - 25: 0, # 'ó' - 24: 0, # 'ö' - 31: 0, # 'ú' - 29: 0, # 'ü' - 42: 0, # 'ő' - 56: 0, # 'ű' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 28, # 'A' - 66: 40, # 'B' - 67: 54, # 'C' - 68: 45, # 'D' - 69: 32, # 'E' - 70: 50, # 'F' - 71: 49, # 'G' - 72: 38, # 'H' - 73: 39, # 'I' - 74: 53, # 'J' - 75: 36, # 'K' - 76: 41, # 'L' - 77: 34, # 'M' - 78: 35, # 'N' - 79: 47, # 'O' - 80: 46, # 'P' - 81: 72, # 'Q' - 82: 43, # 'R' - 83: 33, # 'S' - 84: 37, # 'T' - 85: 57, # 'U' - 86: 48, # 'V' - 87: 64, # 'W' - 88: 68, # 'X' - 89: 55, # 'Y' - 90: 52, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 2, # 'a' - 98: 18, # 'b' - 99: 26, # 'c' - 100: 17, # 'd' - 101: 1, # 'e' - 102: 27, # 'f' - 103: 12, # 'g' - 104: 20, # 'h' - 105: 9, # 'i' - 106: 22, # 'j' - 107: 7, # 'k' - 108: 6, # 'l' - 109: 13, # 'm' - 110: 4, # 'n' - 111: 8, # 'o' - 112: 23, # 'p' - 113: 67, # 'q' - 114: 10, # 'r' - 115: 5, # 's' - 116: 3, # 't' - 117: 21, # 'u' - 118: 19, # 'v' - 119: 65, # 'w' - 120: 62, # 'x' - 121: 16, # 'y' - 122: 11, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 161, # '€' - 129: 162, # None - 130: 163, # '‚' - 131: 164, # None - 132: 165, # '„' - 133: 166, # '…' - 134: 167, # '†' - 135: 168, # '‡' - 136: 169, # None - 137: 170, # '‰' - 138: 171, # 'Š' - 139: 172, # '‹' - 140: 173, # 'Ś' - 141: 174, # 'Ť' - 142: 175, # 'Ž' - 143: 176, # 'Ź' - 144: 177, # None - 145: 178, # '‘' - 146: 179, # '’' - 147: 180, # '“' - 148: 78, # '”' - 149: 181, # '•' - 150: 69, # '–' - 151: 182, # '—' - 152: 183, # None - 153: 184, # '™' - 154: 185, # 'š' - 155: 186, # '›' - 156: 187, # 'ś' - 157: 188, # 'ť' - 158: 189, # 'ž' - 159: 190, # 'ź' - 160: 191, # '\xa0' - 161: 192, # 'ˇ' - 162: 193, # '˘' - 163: 194, # 'Ł' - 164: 195, # '¤' - 165: 196, # 'Ą' - 166: 197, # '¦' - 167: 76, # '§' - 168: 198, # '¨' - 169: 199, # '©' - 170: 200, # 'Ş' - 171: 201, # '«' - 172: 202, # '¬' - 173: 203, # '\xad' - 174: 204, # '®' - 175: 205, # 'Ż' - 176: 81, # '°' - 177: 206, # '±' - 178: 207, # '˛' - 179: 208, # 'ł' - 180: 209, # '´' - 181: 210, # 'µ' - 182: 211, # '¶' - 183: 212, # '·' - 184: 213, # '¸' - 185: 214, # 'ą' - 186: 215, # 'ş' - 187: 216, # '»' - 188: 217, # 'Ľ' - 189: 218, # '˝' - 190: 219, # 'ľ' - 191: 220, # 'ż' - 192: 221, # 'Ŕ' - 193: 51, # 'Á' - 194: 83, # 'Â' - 195: 222, # 'Ă' - 196: 80, # 'Ä' - 197: 223, # 'Ĺ' - 198: 224, # 'Ć' - 199: 225, # 'Ç' - 200: 226, # 'Č' - 201: 44, # 'É' - 202: 227, # 'Ę' - 203: 228, # 'Ë' - 204: 229, # 'Ě' - 205: 61, # 'Í' - 206: 230, # 'Î' - 207: 231, # 'Ď' - 208: 232, # 'Đ' - 209: 233, # 'Ń' - 210: 234, # 'Ň' - 211: 58, # 'Ó' - 212: 235, # 'Ô' - 213: 66, # 'Ő' - 214: 59, # 'Ö' - 215: 236, # '×' - 216: 237, # 'Ř' - 217: 238, # 'Ů' - 218: 60, # 'Ú' - 219: 70, # 'Ű' - 220: 63, # 'Ü' - 221: 239, # 'Ý' - 222: 240, # 'Ţ' - 223: 241, # 'ß' - 224: 84, # 'ŕ' - 225: 14, # 'á' - 226: 75, # 'â' - 227: 242, # 'ă' - 228: 71, # 'ä' - 229: 82, # 'ĺ' - 230: 243, # 'ć' - 231: 73, # 'ç' - 232: 244, # 'č' - 233: 15, # 'é' - 234: 85, # 'ę' - 235: 79, # 'ë' - 236: 86, # 'ě' - 237: 30, # 'í' - 238: 77, # 'î' - 239: 87, # 'ď' - 240: 245, # 'đ' - 241: 246, # 'ń' - 242: 247, # 'ň' - 243: 25, # 'ó' - 244: 74, # 'ô' - 245: 42, # 'ő' - 246: 24, # 'ö' - 247: 248, # '÷' - 248: 249, # 'ř' - 249: 250, # 'ů' - 250: 31, # 'ú' - 251: 56, # 'ű' - 252: 29, # 'ü' - 253: 251, # 'ý' - 254: 252, # 'ţ' - 255: 253, # '˙' -} - -WINDOWS_1250_HUNGARIAN_MODEL = SingleByteCharSetModel( - charset_name="windows-1250", - language="Hungarian", - char_to_order_map=WINDOWS_1250_HUNGARIAN_CHAR_TO_ORDER, - language_model=HUNGARIAN_LANG_MODEL, - typical_positive_ratio=0.947368, - keep_ascii_letters=True, - alphabet="ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű", -) - -ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 28, # 'A' - 66: 40, # 'B' - 67: 54, # 'C' - 68: 45, # 'D' - 69: 32, # 'E' - 70: 50, # 'F' - 71: 49, # 'G' - 72: 38, # 'H' - 73: 39, # 'I' - 74: 53, # 'J' - 75: 36, # 'K' - 76: 41, # 'L' - 77: 34, # 'M' - 78: 35, # 'N' - 79: 47, # 'O' - 80: 46, # 'P' - 81: 71, # 'Q' - 82: 43, # 'R' - 83: 33, # 'S' - 84: 37, # 'T' - 85: 57, # 'U' - 86: 48, # 'V' - 87: 64, # 'W' - 88: 68, # 'X' - 89: 55, # 'Y' - 90: 52, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 2, # 'a' - 98: 18, # 'b' - 99: 26, # 'c' - 100: 17, # 'd' - 101: 1, # 'e' - 102: 27, # 'f' - 103: 12, # 'g' - 104: 20, # 'h' - 105: 9, # 'i' - 106: 22, # 'j' - 107: 7, # 'k' - 108: 6, # 'l' - 109: 13, # 'm' - 110: 4, # 'n' - 111: 8, # 'o' - 112: 23, # 'p' - 113: 67, # 'q' - 114: 10, # 'r' - 115: 5, # 's' - 116: 3, # 't' - 117: 21, # 'u' - 118: 19, # 'v' - 119: 65, # 'w' - 120: 62, # 'x' - 121: 16, # 'y' - 122: 11, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 159, # '\x80' - 129: 160, # '\x81' - 130: 161, # '\x82' - 131: 162, # '\x83' - 132: 163, # '\x84' - 133: 164, # '\x85' - 134: 165, # '\x86' - 135: 166, # '\x87' - 136: 167, # '\x88' - 137: 168, # '\x89' - 138: 169, # '\x8a' - 139: 170, # '\x8b' - 140: 171, # '\x8c' - 141: 172, # '\x8d' - 142: 173, # '\x8e' - 143: 174, # '\x8f' - 144: 175, # '\x90' - 145: 176, # '\x91' - 146: 177, # '\x92' - 147: 178, # '\x93' - 148: 179, # '\x94' - 149: 180, # '\x95' - 150: 181, # '\x96' - 151: 182, # '\x97' - 152: 183, # '\x98' - 153: 184, # '\x99' - 154: 185, # '\x9a' - 155: 186, # '\x9b' - 156: 187, # '\x9c' - 157: 188, # '\x9d' - 158: 189, # '\x9e' - 159: 190, # '\x9f' - 160: 191, # '\xa0' - 161: 192, # 'Ą' - 162: 193, # '˘' - 163: 194, # 'Ł' - 164: 195, # '¤' - 165: 196, # 'Ľ' - 166: 197, # 'Ś' - 167: 75, # '§' - 168: 198, # '¨' - 169: 199, # 'Š' - 170: 200, # 'Ş' - 171: 201, # 'Ť' - 172: 202, # 'Ź' - 173: 203, # '\xad' - 174: 204, # 'Ž' - 175: 205, # 'Ż' - 176: 79, # '°' - 177: 206, # 'ą' - 178: 207, # '˛' - 179: 208, # 'ł' - 180: 209, # '´' - 181: 210, # 'ľ' - 182: 211, # 'ś' - 183: 212, # 'ˇ' - 184: 213, # '¸' - 185: 214, # 'š' - 186: 215, # 'ş' - 187: 216, # 'ť' - 188: 217, # 'ź' - 189: 218, # '˝' - 190: 219, # 'ž' - 191: 220, # 'ż' - 192: 221, # 'Ŕ' - 193: 51, # 'Á' - 194: 81, # 'Â' - 195: 222, # 'Ă' - 196: 78, # 'Ä' - 197: 223, # 'Ĺ' - 198: 224, # 'Ć' - 199: 225, # 'Ç' - 200: 226, # 'Č' - 201: 44, # 'É' - 202: 227, # 'Ę' - 203: 228, # 'Ë' - 204: 229, # 'Ě' - 205: 61, # 'Í' - 206: 230, # 'Î' - 207: 231, # 'Ď' - 208: 232, # 'Đ' - 209: 233, # 'Ń' - 210: 234, # 'Ň' - 211: 58, # 'Ó' - 212: 235, # 'Ô' - 213: 66, # 'Ő' - 214: 59, # 'Ö' - 215: 236, # '×' - 216: 237, # 'Ř' - 217: 238, # 'Ů' - 218: 60, # 'Ú' - 219: 69, # 'Ű' - 220: 63, # 'Ü' - 221: 239, # 'Ý' - 222: 240, # 'Ţ' - 223: 241, # 'ß' - 224: 82, # 'ŕ' - 225: 14, # 'á' - 226: 74, # 'â' - 227: 242, # 'ă' - 228: 70, # 'ä' - 229: 80, # 'ĺ' - 230: 243, # 'ć' - 231: 72, # 'ç' - 232: 244, # 'č' - 233: 15, # 'é' - 234: 83, # 'ę' - 235: 77, # 'ë' - 236: 84, # 'ě' - 237: 30, # 'í' - 238: 76, # 'î' - 239: 85, # 'ď' - 240: 245, # 'đ' - 241: 246, # 'ń' - 242: 247, # 'ň' - 243: 25, # 'ó' - 244: 73, # 'ô' - 245: 42, # 'ő' - 246: 24, # 'ö' - 247: 248, # '÷' - 248: 249, # 'ř' - 249: 250, # 'ů' - 250: 31, # 'ú' - 251: 56, # 'ű' - 252: 29, # 'ü' - 253: 251, # 'ý' - 254: 252, # 'ţ' - 255: 253, # '˙' -} - -ISO_8859_2_HUNGARIAN_MODEL = SingleByteCharSetModel( - charset_name="ISO-8859-2", - language="Hungarian", - char_to_order_map=ISO_8859_2_HUNGARIAN_CHAR_TO_ORDER, - language_model=HUNGARIAN_LANG_MODEL, - typical_positive_ratio=0.947368, - keep_ascii_letters=True, - alphabet="ABCDEFGHIJKLMNOPRSTUVZabcdefghijklmnoprstuvzÁÉÍÓÖÚÜáéíóöúüŐőŰű", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langrussianmodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langrussianmodel.py deleted file mode 100644 index 0d5b178..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langrussianmodel.py +++ /dev/null @@ -1,5725 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -RUSSIAN_LANG_MODEL = { - 37: { # 'А' - 37: 0, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 1, # 'Ж' - 51: 1, # 'З' - 42: 1, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 2, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 1, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 1, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 1, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 0, # 'е' - 24: 1, # 'ж' - 20: 1, # 'з' - 4: 0, # 'и' - 23: 1, # 'й' - 11: 2, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 0, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 2, # 'у' - 39: 2, # 'ф' - 26: 2, # 'х' - 28: 0, # 'ц' - 22: 1, # 'ч' - 25: 2, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 44: { # 'Б' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 1, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 2, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 33: { # 'В' - 37: 2, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 1, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 2, # 'а' - 21: 1, # 'б' - 10: 1, # 'в' - 19: 1, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 2, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 2, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 1, # 'ц' - 22: 2, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 1, # 'ъ' - 18: 3, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 0, # 'ю' - 16: 1, # 'я' - }, - 46: { # 'Г' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 2, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 1, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 1, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 41: { # 'Д' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 2, # 'Е' - 56: 1, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 2, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 3, # 'ж' - 20: 1, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 1, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 48: { # 'Е' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 1, # 'Ж' - 51: 1, # 'З' - 42: 1, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 2, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 2, # 'Р' - 32: 2, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 1, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 0, # 'а' - 21: 0, # 'б' - 10: 2, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 2, # 'е' - 24: 1, # 'ж' - 20: 1, # 'з' - 4: 0, # 'и' - 23: 2, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 1, # 'н' - 1: 0, # 'о' - 15: 1, # 'п' - 9: 1, # 'р' - 7: 3, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 2, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 56: { # 'Ж' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 1, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 1, # 'б' - 10: 0, # 'в' - 19: 1, # 'г' - 13: 1, # 'д' - 2: 2, # 'е' - 24: 1, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 1, # 'м' - 5: 0, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 2, # 'ю' - 16: 0, # 'я' - }, - 51: { # 'З' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 0, # 'г' - 13: 2, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 1, # 'л' - 12: 1, # 'м' - 5: 2, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 1, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 1, # 'я' - }, - 42: { # 'И' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 2, # 'Е' - 56: 1, # 'Ж' - 51: 1, # 'З' - 42: 1, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 2, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 1, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 1, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 2, # 'з' - 4: 1, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 1, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 1, # 'у' - 39: 1, # 'ф' - 26: 2, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 60: { # 'Й' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 1, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 0, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 36: { # 'К' - 37: 2, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 1, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 2, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 1, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 0, # 'м' - 5: 1, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 49: { # 'Л' - 37: 2, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 1, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 1, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 0, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 0, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 1, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 1, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 1, # 'л' - 12: 0, # 'м' - 5: 1, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 2, # 'ю' - 16: 1, # 'я' - }, - 38: { # 'М' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 1, # 'Ф' - 55: 1, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 0, # 'Ь' - 47: 1, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 1, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 1, # 'л' - 12: 1, # 'м' - 5: 2, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 1, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 31: { # 'Н' - 37: 2, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 1, # 'З' - 42: 2, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 1, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 1, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 3, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 2, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 34: { # 'О' - 37: 0, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 2, # 'Д' - 48: 1, # 'Е' - 56: 1, # 'Ж' - 51: 1, # 'З' - 42: 1, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 2, # 'Л' - 38: 1, # 'М' - 31: 2, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 2, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 1, # 'Ф' - 55: 1, # 'Х' - 58: 0, # 'Ц' - 50: 1, # 'Ч' - 57: 1, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 1, # 'а' - 21: 2, # 'б' - 10: 1, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 0, # 'е' - 24: 1, # 'ж' - 20: 1, # 'з' - 4: 0, # 'и' - 23: 1, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 3, # 'н' - 1: 0, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 1, # 'у' - 39: 1, # 'ф' - 26: 2, # 'х' - 28: 1, # 'ц' - 22: 2, # 'ч' - 25: 2, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 35: { # 'П' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 2, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 1, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 0, # 'м' - 5: 1, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 3, # 'р' - 7: 1, # 'с' - 6: 1, # 'т' - 14: 2, # 'у' - 39: 1, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 2, # 'ь' - 30: 1, # 'э' - 27: 0, # 'ю' - 16: 2, # 'я' - }, - 45: { # 'Р' - 37: 2, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 2, # 'Е' - 56: 1, # 'Ж' - 51: 0, # 'З' - 42: 2, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 2, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 1, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 1, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 1, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 2, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 2, # 'я' - }, - 32: { # 'С' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 2, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 1, # 'Ч' - 57: 1, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 1, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 2, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 1, # 'ж' - 20: 1, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 2, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 1, # 'с' - 6: 3, # 'т' - 14: 2, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 1, # 'ц' - 22: 1, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 1, # 'ъ' - 18: 1, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 40: { # 'Т' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 2, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 1, # 'Ь' - 47: 1, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 1, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 1, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 52: { # 'У' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 1, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 0, # 'Ц' - 50: 1, # 'Ч' - 57: 1, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 1, # 'Ю' - 43: 0, # 'Я' - 3: 1, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 2, # 'д' - 2: 1, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 2, # 'и' - 23: 1, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 1, # 'н' - 1: 2, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 0, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 1, # 'ц' - 22: 2, # 'ч' - 25: 1, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 53: { # 'Ф' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 0, # 'с' - 6: 1, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 55: { # 'Х' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 2, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 0, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 1, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 1, # 'ь' - 30: 1, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 58: { # 'Ц' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 1, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 1, # 'а' - 21: 0, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 0, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 1, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 50: { # 'Ч' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 1, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 1, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 1, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 1, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 3, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 1, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 57: { # 'Ш' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 1, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 0, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 1, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 1, # 'н' - 1: 2, # 'о' - 15: 2, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 2, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 63: { # 'Щ' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 1, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 1, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 1, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 1, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 1, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 1, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 62: { # 'Ы' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 1, # 'Ц' - 50: 0, # 'Ч' - 57: 1, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 0, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 0, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 0, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 61: { # 'Ь' - 37: 0, # 'А' - 44: 1, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 0, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 1, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 1, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 1, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 1, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 0, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 0, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 0, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 0, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 47: { # 'Э' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 1, # 'Й' - 36: 1, # 'К' - 49: 1, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 1, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 1, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 2, # 'д' - 2: 0, # 'е' - 24: 1, # 'ж' - 20: 0, # 'з' - 4: 0, # 'и' - 23: 2, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 0, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 1, # 'с' - 6: 3, # 'т' - 14: 1, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 59: { # 'Ю' - 37: 1, # 'А' - 44: 1, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 1, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 0, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 1, # 'б' - 10: 0, # 'в' - 19: 1, # 'г' - 13: 1, # 'д' - 2: 0, # 'е' - 24: 1, # 'ж' - 20: 0, # 'з' - 4: 0, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 2, # 'н' - 1: 0, # 'о' - 15: 1, # 'п' - 9: 1, # 'р' - 7: 1, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 43: { # 'Я' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 1, # 'В' - 46: 1, # 'Г' - 41: 0, # 'Д' - 48: 1, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 1, # 'С' - 40: 1, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 1, # 'Х' - 58: 0, # 'Ц' - 50: 1, # 'Ч' - 57: 0, # 'Ш' - 63: 1, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 1, # 'Ю' - 43: 1, # 'Я' - 3: 0, # 'а' - 21: 1, # 'б' - 10: 1, # 'в' - 19: 1, # 'г' - 13: 1, # 'д' - 2: 0, # 'е' - 24: 0, # 'ж' - 20: 1, # 'з' - 4: 0, # 'и' - 23: 1, # 'й' - 11: 1, # 'к' - 8: 1, # 'л' - 12: 1, # 'м' - 5: 2, # 'н' - 1: 0, # 'о' - 15: 1, # 'п' - 9: 1, # 'р' - 7: 1, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 0, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 3: { # 'а' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 1, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 3, # 'з' - 4: 3, # 'и' - 23: 3, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 2, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 3, # 'ц' - 22: 3, # 'ч' - 25: 3, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 2, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 21: { # 'б' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 1, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 1, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 0, # 'ф' - 26: 2, # 'х' - 28: 1, # 'ц' - 22: 1, # 'ч' - 25: 2, # 'ш' - 29: 3, # 'щ' - 54: 2, # 'ъ' - 18: 3, # 'ы' - 17: 2, # 'ь' - 30: 1, # 'э' - 27: 2, # 'ю' - 16: 3, # 'я' - }, - 10: { # 'в' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 2, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 1, # 'ж' - 20: 3, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 2, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 3, # 'ш' - 29: 2, # 'щ' - 54: 2, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 3, # 'я' - }, - 19: { # 'г' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 3, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 1, # 'ц' - 22: 2, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 1, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 13: { # 'д' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 3, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 2, # 'х' - 28: 3, # 'ц' - 22: 2, # 'ч' - 25: 2, # 'ш' - 29: 1, # 'щ' - 54: 2, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 1, # 'э' - 27: 2, # 'ю' - 16: 3, # 'я' - }, - 2: { # 'е' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 3, # 'з' - 4: 2, # 'и' - 23: 3, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 2, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 3, # 'ц' - 22: 3, # 'ч' - 25: 3, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 2, # 'ю' - 16: 3, # 'я' - }, - 24: { # 'ж' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 1, # 'в' - 19: 2, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 3, # 'н' - 1: 2, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 1, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 0, # 'х' - 28: 1, # 'ц' - 22: 2, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 2, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 20: { # 'з' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 3, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 1, # 'ц' - 22: 2, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 2, # 'ъ' - 18: 3, # 'ы' - 17: 2, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 3, # 'я' - }, - 4: { # 'и' - 37: 1, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 3, # 'з' - 4: 3, # 'и' - 23: 3, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 2, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 3, # 'ц' - 22: 3, # 'ч' - 25: 3, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 2, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 23: { # 'й' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 1, # 'а' - 21: 1, # 'б' - 10: 1, # 'в' - 19: 2, # 'г' - 13: 3, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 2, # 'з' - 4: 1, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 2, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 1, # 'у' - 39: 2, # 'ф' - 26: 1, # 'х' - 28: 2, # 'ц' - 22: 3, # 'ч' - 25: 2, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 2, # 'я' - }, - 11: { # 'к' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 3, # 'в' - 19: 1, # 'г' - 13: 1, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 3, # 'л' - 12: 1, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 2, # 'х' - 28: 2, # 'ц' - 22: 1, # 'ч' - 25: 2, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 1, # 'ы' - 17: 1, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 8: { # 'л' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 3, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 1, # 'р' - 7: 3, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 2, # 'х' - 28: 1, # 'ц' - 22: 3, # 'ч' - 25: 2, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 1, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 12: { # 'м' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 2, # 'г' - 13: 1, # 'д' - 2: 3, # 'е' - 24: 1, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 2, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 1, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 2, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 3, # 'я' - }, - 5: { # 'н' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 2, # 'х' - 28: 3, # 'ц' - 22: 3, # 'ч' - 25: 2, # 'ш' - 29: 2, # 'щ' - 54: 1, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 1, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 1: { # 'о' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 3, # 'з' - 4: 3, # 'и' - 23: 3, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 2, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 2, # 'ц' - 22: 3, # 'ч' - 25: 3, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 2, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 15: { # 'п' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 3, # 'л' - 12: 1, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 3, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 0, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 1, # 'ш' - 29: 1, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 2, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 3, # 'я' - }, - 9: { # 'р' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 2, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 3, # 'ш' - 29: 2, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 2, # 'э' - 27: 2, # 'ю' - 16: 3, # 'я' - }, - 7: { # 'с' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 1, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 3, # 'в' - 19: 2, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 2, # 'ц' - 22: 3, # 'ч' - 25: 2, # 'ш' - 29: 1, # 'щ' - 54: 2, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 2, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 6: { # 'т' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 2, # 'б' - 10: 3, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 1, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 2, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 2, # 'ш' - 29: 2, # 'щ' - 54: 2, # 'ъ' - 18: 3, # 'ы' - 17: 3, # 'ь' - 30: 2, # 'э' - 27: 2, # 'ю' - 16: 3, # 'я' - }, - 14: { # 'у' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 3, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 3, # 'з' - 4: 2, # 'и' - 23: 2, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 2, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 1, # 'у' - 39: 2, # 'ф' - 26: 3, # 'х' - 28: 2, # 'ц' - 22: 3, # 'ч' - 25: 3, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 2, # 'э' - 27: 3, # 'ю' - 16: 2, # 'я' - }, - 39: { # 'ф' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 0, # 'в' - 19: 1, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 1, # 'н' - 1: 3, # 'о' - 15: 1, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 2, # 'у' - 39: 2, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 1, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 2, # 'ы' - 17: 1, # 'ь' - 30: 2, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 26: { # 'х' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 3, # 'в' - 19: 1, # 'г' - 13: 1, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 1, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 1, # 'п' - 9: 3, # 'р' - 7: 2, # 'с' - 6: 2, # 'т' - 14: 2, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 1, # 'ц' - 22: 1, # 'ч' - 25: 2, # 'ш' - 29: 0, # 'щ' - 54: 1, # 'ъ' - 18: 0, # 'ы' - 17: 1, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 28: { # 'ц' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 1, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 1, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 2, # 'к' - 8: 1, # 'л' - 12: 1, # 'м' - 5: 1, # 'н' - 1: 3, # 'о' - 15: 0, # 'п' - 9: 1, # 'р' - 7: 0, # 'с' - 6: 1, # 'т' - 14: 3, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 1, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 3, # 'ы' - 17: 1, # 'ь' - 30: 0, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 22: { # 'ч' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 1, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 2, # 'л' - 12: 1, # 'м' - 5: 3, # 'н' - 1: 2, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 1, # 'с' - 6: 3, # 'т' - 14: 3, # 'у' - 39: 1, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 1, # 'ч' - 25: 2, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 3, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 25: { # 'ш' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 1, # 'б' - 10: 2, # 'в' - 19: 1, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 2, # 'м' - 5: 3, # 'н' - 1: 3, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 1, # 'с' - 6: 2, # 'т' - 14: 3, # 'у' - 39: 2, # 'ф' - 26: 1, # 'х' - 28: 1, # 'ц' - 22: 1, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 3, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 0, # 'я' - }, - 29: { # 'щ' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 3, # 'а' - 21: 0, # 'б' - 10: 1, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 3, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 3, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 1, # 'м' - 5: 2, # 'н' - 1: 1, # 'о' - 15: 0, # 'п' - 9: 2, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 2, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 2, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 0, # 'я' - }, - 54: { # 'ъ' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 0, # 'б' - 10: 0, # 'в' - 19: 0, # 'г' - 13: 0, # 'д' - 2: 2, # 'е' - 24: 0, # 'ж' - 20: 0, # 'з' - 4: 0, # 'и' - 23: 0, # 'й' - 11: 0, # 'к' - 8: 0, # 'л' - 12: 0, # 'м' - 5: 0, # 'н' - 1: 0, # 'о' - 15: 0, # 'п' - 9: 0, # 'р' - 7: 0, # 'с' - 6: 0, # 'т' - 14: 0, # 'у' - 39: 0, # 'ф' - 26: 0, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 0, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 1, # 'ю' - 16: 2, # 'я' - }, - 18: { # 'ы' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 3, # 'б' - 10: 3, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 2, # 'и' - 23: 3, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 1, # 'о' - 15: 3, # 'п' - 9: 3, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 1, # 'у' - 39: 0, # 'ф' - 26: 3, # 'х' - 28: 2, # 'ц' - 22: 3, # 'ч' - 25: 3, # 'ш' - 29: 2, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 0, # 'ю' - 16: 2, # 'я' - }, - 17: { # 'ь' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 2, # 'б' - 10: 2, # 'в' - 19: 2, # 'г' - 13: 2, # 'д' - 2: 3, # 'е' - 24: 1, # 'ж' - 20: 3, # 'з' - 4: 2, # 'и' - 23: 0, # 'й' - 11: 3, # 'к' - 8: 0, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 2, # 'о' - 15: 2, # 'п' - 9: 1, # 'р' - 7: 3, # 'с' - 6: 2, # 'т' - 14: 0, # 'у' - 39: 2, # 'ф' - 26: 1, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 3, # 'ш' - 29: 2, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 3, # 'ю' - 16: 3, # 'я' - }, - 30: { # 'э' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 1, # 'М' - 31: 1, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 1, # 'Р' - 32: 1, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 1, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 1, # 'б' - 10: 1, # 'в' - 19: 1, # 'г' - 13: 2, # 'д' - 2: 1, # 'е' - 24: 0, # 'ж' - 20: 1, # 'з' - 4: 0, # 'и' - 23: 2, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 0, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 2, # 'с' - 6: 3, # 'т' - 14: 1, # 'у' - 39: 2, # 'ф' - 26: 1, # 'х' - 28: 0, # 'ц' - 22: 0, # 'ч' - 25: 1, # 'ш' - 29: 0, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 1, # 'ю' - 16: 1, # 'я' - }, - 27: { # 'ю' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 2, # 'а' - 21: 3, # 'б' - 10: 1, # 'в' - 19: 2, # 'г' - 13: 3, # 'д' - 2: 1, # 'е' - 24: 2, # 'ж' - 20: 2, # 'з' - 4: 1, # 'и' - 23: 1, # 'й' - 11: 2, # 'к' - 8: 2, # 'л' - 12: 2, # 'м' - 5: 2, # 'н' - 1: 1, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 0, # 'у' - 39: 1, # 'ф' - 26: 2, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 2, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 1, # 'э' - 27: 2, # 'ю' - 16: 1, # 'я' - }, - 16: { # 'я' - 37: 0, # 'А' - 44: 0, # 'Б' - 33: 0, # 'В' - 46: 0, # 'Г' - 41: 0, # 'Д' - 48: 0, # 'Е' - 56: 0, # 'Ж' - 51: 0, # 'З' - 42: 0, # 'И' - 60: 0, # 'Й' - 36: 0, # 'К' - 49: 0, # 'Л' - 38: 0, # 'М' - 31: 0, # 'Н' - 34: 0, # 'О' - 35: 0, # 'П' - 45: 0, # 'Р' - 32: 0, # 'С' - 40: 0, # 'Т' - 52: 0, # 'У' - 53: 0, # 'Ф' - 55: 0, # 'Х' - 58: 0, # 'Ц' - 50: 0, # 'Ч' - 57: 0, # 'Ш' - 63: 0, # 'Щ' - 62: 0, # 'Ы' - 61: 0, # 'Ь' - 47: 0, # 'Э' - 59: 0, # 'Ю' - 43: 0, # 'Я' - 3: 0, # 'а' - 21: 2, # 'б' - 10: 3, # 'в' - 19: 2, # 'г' - 13: 3, # 'д' - 2: 3, # 'е' - 24: 3, # 'ж' - 20: 3, # 'з' - 4: 2, # 'и' - 23: 2, # 'й' - 11: 3, # 'к' - 8: 3, # 'л' - 12: 3, # 'м' - 5: 3, # 'н' - 1: 0, # 'о' - 15: 2, # 'п' - 9: 2, # 'р' - 7: 3, # 'с' - 6: 3, # 'т' - 14: 1, # 'у' - 39: 1, # 'ф' - 26: 3, # 'х' - 28: 2, # 'ц' - 22: 2, # 'ч' - 25: 2, # 'ш' - 29: 3, # 'щ' - 54: 0, # 'ъ' - 18: 0, # 'ы' - 17: 0, # 'ь' - 30: 0, # 'э' - 27: 2, # 'ю' - 16: 2, # 'я' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -IBM866_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 37, # 'А' - 129: 44, # 'Б' - 130: 33, # 'В' - 131: 46, # 'Г' - 132: 41, # 'Д' - 133: 48, # 'Е' - 134: 56, # 'Ж' - 135: 51, # 'З' - 136: 42, # 'И' - 137: 60, # 'Й' - 138: 36, # 'К' - 139: 49, # 'Л' - 140: 38, # 'М' - 141: 31, # 'Н' - 142: 34, # 'О' - 143: 35, # 'П' - 144: 45, # 'Р' - 145: 32, # 'С' - 146: 40, # 'Т' - 147: 52, # 'У' - 148: 53, # 'Ф' - 149: 55, # 'Х' - 150: 58, # 'Ц' - 151: 50, # 'Ч' - 152: 57, # 'Ш' - 153: 63, # 'Щ' - 154: 70, # 'Ъ' - 155: 62, # 'Ы' - 156: 61, # 'Ь' - 157: 47, # 'Э' - 158: 59, # 'Ю' - 159: 43, # 'Я' - 160: 3, # 'а' - 161: 21, # 'б' - 162: 10, # 'в' - 163: 19, # 'г' - 164: 13, # 'д' - 165: 2, # 'е' - 166: 24, # 'ж' - 167: 20, # 'з' - 168: 4, # 'и' - 169: 23, # 'й' - 170: 11, # 'к' - 171: 8, # 'л' - 172: 12, # 'м' - 173: 5, # 'н' - 174: 1, # 'о' - 175: 15, # 'п' - 176: 191, # '░' - 177: 192, # '▒' - 178: 193, # '▓' - 179: 194, # '│' - 180: 195, # '┤' - 181: 196, # '╡' - 182: 197, # '╢' - 183: 198, # '╖' - 184: 199, # '╕' - 185: 200, # '╣' - 186: 201, # '║' - 187: 202, # '╗' - 188: 203, # '╝' - 189: 204, # '╜' - 190: 205, # '╛' - 191: 206, # '┐' - 192: 207, # '└' - 193: 208, # '┴' - 194: 209, # '┬' - 195: 210, # '├' - 196: 211, # '─' - 197: 212, # '┼' - 198: 213, # '╞' - 199: 214, # '╟' - 200: 215, # '╚' - 201: 216, # '╔' - 202: 217, # '╩' - 203: 218, # '╦' - 204: 219, # '╠' - 205: 220, # '═' - 206: 221, # '╬' - 207: 222, # '╧' - 208: 223, # '╨' - 209: 224, # '╤' - 210: 225, # '╥' - 211: 226, # '╙' - 212: 227, # '╘' - 213: 228, # '╒' - 214: 229, # '╓' - 215: 230, # '╫' - 216: 231, # '╪' - 217: 232, # '┘' - 218: 233, # '┌' - 219: 234, # '█' - 220: 235, # '▄' - 221: 236, # '▌' - 222: 237, # '▐' - 223: 238, # '▀' - 224: 9, # 'р' - 225: 7, # 'с' - 226: 6, # 'т' - 227: 14, # 'у' - 228: 39, # 'ф' - 229: 26, # 'х' - 230: 28, # 'ц' - 231: 22, # 'ч' - 232: 25, # 'ш' - 233: 29, # 'щ' - 234: 54, # 'ъ' - 235: 18, # 'ы' - 236: 17, # 'ь' - 237: 30, # 'э' - 238: 27, # 'ю' - 239: 16, # 'я' - 240: 239, # 'Ё' - 241: 68, # 'ё' - 242: 240, # 'Є' - 243: 241, # 'є' - 244: 242, # 'Ї' - 245: 243, # 'ї' - 246: 244, # 'Ў' - 247: 245, # 'ў' - 248: 246, # '°' - 249: 247, # '∙' - 250: 248, # '·' - 251: 249, # '√' - 252: 250, # '№' - 253: 251, # '¤' - 254: 252, # '■' - 255: 255, # '\xa0' -} - -IBM866_RUSSIAN_MODEL = SingleByteCharSetModel( - charset_name="IBM866", - language="Russian", - char_to_order_map=IBM866_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", -) - -WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # 'Ђ' - 129: 192, # 'Ѓ' - 130: 193, # '‚' - 131: 194, # 'ѓ' - 132: 195, # '„' - 133: 196, # '…' - 134: 197, # '†' - 135: 198, # '‡' - 136: 199, # '€' - 137: 200, # '‰' - 138: 201, # 'Љ' - 139: 202, # '‹' - 140: 203, # 'Њ' - 141: 204, # 'Ќ' - 142: 205, # 'Ћ' - 143: 206, # 'Џ' - 144: 207, # 'ђ' - 145: 208, # '‘' - 146: 209, # '’' - 147: 210, # '“' - 148: 211, # '”' - 149: 212, # '•' - 150: 213, # '–' - 151: 214, # '—' - 152: 215, # None - 153: 216, # '™' - 154: 217, # 'љ' - 155: 218, # '›' - 156: 219, # 'њ' - 157: 220, # 'ќ' - 158: 221, # 'ћ' - 159: 222, # 'џ' - 160: 223, # '\xa0' - 161: 224, # 'Ў' - 162: 225, # 'ў' - 163: 226, # 'Ј' - 164: 227, # '¤' - 165: 228, # 'Ґ' - 166: 229, # '¦' - 167: 230, # '§' - 168: 231, # 'Ё' - 169: 232, # '©' - 170: 233, # 'Є' - 171: 234, # '«' - 172: 235, # '¬' - 173: 236, # '\xad' - 174: 237, # '®' - 175: 238, # 'Ї' - 176: 239, # '°' - 177: 240, # '±' - 178: 241, # 'І' - 179: 242, # 'і' - 180: 243, # 'ґ' - 181: 244, # 'µ' - 182: 245, # '¶' - 183: 246, # '·' - 184: 68, # 'ё' - 185: 247, # '№' - 186: 248, # 'є' - 187: 249, # '»' - 188: 250, # 'ј' - 189: 251, # 'Ѕ' - 190: 252, # 'ѕ' - 191: 253, # 'ї' - 192: 37, # 'А' - 193: 44, # 'Б' - 194: 33, # 'В' - 195: 46, # 'Г' - 196: 41, # 'Д' - 197: 48, # 'Е' - 198: 56, # 'Ж' - 199: 51, # 'З' - 200: 42, # 'И' - 201: 60, # 'Й' - 202: 36, # 'К' - 203: 49, # 'Л' - 204: 38, # 'М' - 205: 31, # 'Н' - 206: 34, # 'О' - 207: 35, # 'П' - 208: 45, # 'Р' - 209: 32, # 'С' - 210: 40, # 'Т' - 211: 52, # 'У' - 212: 53, # 'Ф' - 213: 55, # 'Х' - 214: 58, # 'Ц' - 215: 50, # 'Ч' - 216: 57, # 'Ш' - 217: 63, # 'Щ' - 218: 70, # 'Ъ' - 219: 62, # 'Ы' - 220: 61, # 'Ь' - 221: 47, # 'Э' - 222: 59, # 'Ю' - 223: 43, # 'Я' - 224: 3, # 'а' - 225: 21, # 'б' - 226: 10, # 'в' - 227: 19, # 'г' - 228: 13, # 'д' - 229: 2, # 'е' - 230: 24, # 'ж' - 231: 20, # 'з' - 232: 4, # 'и' - 233: 23, # 'й' - 234: 11, # 'к' - 235: 8, # 'л' - 236: 12, # 'м' - 237: 5, # 'н' - 238: 1, # 'о' - 239: 15, # 'п' - 240: 9, # 'р' - 241: 7, # 'с' - 242: 6, # 'т' - 243: 14, # 'у' - 244: 39, # 'ф' - 245: 26, # 'х' - 246: 28, # 'ц' - 247: 22, # 'ч' - 248: 25, # 'ш' - 249: 29, # 'щ' - 250: 54, # 'ъ' - 251: 18, # 'ы' - 252: 17, # 'ь' - 253: 30, # 'э' - 254: 27, # 'ю' - 255: 16, # 'я' -} - -WINDOWS_1251_RUSSIAN_MODEL = SingleByteCharSetModel( - charset_name="windows-1251", - language="Russian", - char_to_order_map=WINDOWS_1251_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", -) - -IBM855_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # 'ђ' - 129: 192, # 'Ђ' - 130: 193, # 'ѓ' - 131: 194, # 'Ѓ' - 132: 68, # 'ё' - 133: 195, # 'Ё' - 134: 196, # 'є' - 135: 197, # 'Є' - 136: 198, # 'ѕ' - 137: 199, # 'Ѕ' - 138: 200, # 'і' - 139: 201, # 'І' - 140: 202, # 'ї' - 141: 203, # 'Ї' - 142: 204, # 'ј' - 143: 205, # 'Ј' - 144: 206, # 'љ' - 145: 207, # 'Љ' - 146: 208, # 'њ' - 147: 209, # 'Њ' - 148: 210, # 'ћ' - 149: 211, # 'Ћ' - 150: 212, # 'ќ' - 151: 213, # 'Ќ' - 152: 214, # 'ў' - 153: 215, # 'Ў' - 154: 216, # 'џ' - 155: 217, # 'Џ' - 156: 27, # 'ю' - 157: 59, # 'Ю' - 158: 54, # 'ъ' - 159: 70, # 'Ъ' - 160: 3, # 'а' - 161: 37, # 'А' - 162: 21, # 'б' - 163: 44, # 'Б' - 164: 28, # 'ц' - 165: 58, # 'Ц' - 166: 13, # 'д' - 167: 41, # 'Д' - 168: 2, # 'е' - 169: 48, # 'Е' - 170: 39, # 'ф' - 171: 53, # 'Ф' - 172: 19, # 'г' - 173: 46, # 'Г' - 174: 218, # '«' - 175: 219, # '»' - 176: 220, # '░' - 177: 221, # '▒' - 178: 222, # '▓' - 179: 223, # '│' - 180: 224, # '┤' - 181: 26, # 'х' - 182: 55, # 'Х' - 183: 4, # 'и' - 184: 42, # 'И' - 185: 225, # '╣' - 186: 226, # '║' - 187: 227, # '╗' - 188: 228, # '╝' - 189: 23, # 'й' - 190: 60, # 'Й' - 191: 229, # '┐' - 192: 230, # '└' - 193: 231, # '┴' - 194: 232, # '┬' - 195: 233, # '├' - 196: 234, # '─' - 197: 235, # '┼' - 198: 11, # 'к' - 199: 36, # 'К' - 200: 236, # '╚' - 201: 237, # '╔' - 202: 238, # '╩' - 203: 239, # '╦' - 204: 240, # '╠' - 205: 241, # '═' - 206: 242, # '╬' - 207: 243, # '¤' - 208: 8, # 'л' - 209: 49, # 'Л' - 210: 12, # 'м' - 211: 38, # 'М' - 212: 5, # 'н' - 213: 31, # 'Н' - 214: 1, # 'о' - 215: 34, # 'О' - 216: 15, # 'п' - 217: 244, # '┘' - 218: 245, # '┌' - 219: 246, # '█' - 220: 247, # '▄' - 221: 35, # 'П' - 222: 16, # 'я' - 223: 248, # '▀' - 224: 43, # 'Я' - 225: 9, # 'р' - 226: 45, # 'Р' - 227: 7, # 'с' - 228: 32, # 'С' - 229: 6, # 'т' - 230: 40, # 'Т' - 231: 14, # 'у' - 232: 52, # 'У' - 233: 24, # 'ж' - 234: 56, # 'Ж' - 235: 10, # 'в' - 236: 33, # 'В' - 237: 17, # 'ь' - 238: 61, # 'Ь' - 239: 249, # '№' - 240: 250, # '\xad' - 241: 18, # 'ы' - 242: 62, # 'Ы' - 243: 20, # 'з' - 244: 51, # 'З' - 245: 25, # 'ш' - 246: 57, # 'Ш' - 247: 30, # 'э' - 248: 47, # 'Э' - 249: 29, # 'щ' - 250: 63, # 'Щ' - 251: 22, # 'ч' - 252: 50, # 'Ч' - 253: 251, # '§' - 254: 252, # '■' - 255: 255, # '\xa0' -} - -IBM855_RUSSIAN_MODEL = SingleByteCharSetModel( - charset_name="IBM855", - language="Russian", - char_to_order_map=IBM855_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", -) - -KOI8_R_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # '─' - 129: 192, # '│' - 130: 193, # '┌' - 131: 194, # '┐' - 132: 195, # '└' - 133: 196, # '┘' - 134: 197, # '├' - 135: 198, # '┤' - 136: 199, # '┬' - 137: 200, # '┴' - 138: 201, # '┼' - 139: 202, # '▀' - 140: 203, # '▄' - 141: 204, # '█' - 142: 205, # '▌' - 143: 206, # '▐' - 144: 207, # '░' - 145: 208, # '▒' - 146: 209, # '▓' - 147: 210, # '⌠' - 148: 211, # '■' - 149: 212, # '∙' - 150: 213, # '√' - 151: 214, # '≈' - 152: 215, # '≤' - 153: 216, # '≥' - 154: 217, # '\xa0' - 155: 218, # '⌡' - 156: 219, # '°' - 157: 220, # '²' - 158: 221, # '·' - 159: 222, # '÷' - 160: 223, # '═' - 161: 224, # '║' - 162: 225, # '╒' - 163: 68, # 'ё' - 164: 226, # '╓' - 165: 227, # '╔' - 166: 228, # '╕' - 167: 229, # '╖' - 168: 230, # '╗' - 169: 231, # '╘' - 170: 232, # '╙' - 171: 233, # '╚' - 172: 234, # '╛' - 173: 235, # '╜' - 174: 236, # '╝' - 175: 237, # '╞' - 176: 238, # '╟' - 177: 239, # '╠' - 178: 240, # '╡' - 179: 241, # 'Ё' - 180: 242, # '╢' - 181: 243, # '╣' - 182: 244, # '╤' - 183: 245, # '╥' - 184: 246, # '╦' - 185: 247, # '╧' - 186: 248, # '╨' - 187: 249, # '╩' - 188: 250, # '╪' - 189: 251, # '╫' - 190: 252, # '╬' - 191: 253, # '©' - 192: 27, # 'ю' - 193: 3, # 'а' - 194: 21, # 'б' - 195: 28, # 'ц' - 196: 13, # 'д' - 197: 2, # 'е' - 198: 39, # 'ф' - 199: 19, # 'г' - 200: 26, # 'х' - 201: 4, # 'и' - 202: 23, # 'й' - 203: 11, # 'к' - 204: 8, # 'л' - 205: 12, # 'м' - 206: 5, # 'н' - 207: 1, # 'о' - 208: 15, # 'п' - 209: 16, # 'я' - 210: 9, # 'р' - 211: 7, # 'с' - 212: 6, # 'т' - 213: 14, # 'у' - 214: 24, # 'ж' - 215: 10, # 'в' - 216: 17, # 'ь' - 217: 18, # 'ы' - 218: 20, # 'з' - 219: 25, # 'ш' - 220: 30, # 'э' - 221: 29, # 'щ' - 222: 22, # 'ч' - 223: 54, # 'ъ' - 224: 59, # 'Ю' - 225: 37, # 'А' - 226: 44, # 'Б' - 227: 58, # 'Ц' - 228: 41, # 'Д' - 229: 48, # 'Е' - 230: 53, # 'Ф' - 231: 46, # 'Г' - 232: 55, # 'Х' - 233: 42, # 'И' - 234: 60, # 'Й' - 235: 36, # 'К' - 236: 49, # 'Л' - 237: 38, # 'М' - 238: 31, # 'Н' - 239: 34, # 'О' - 240: 35, # 'П' - 241: 43, # 'Я' - 242: 45, # 'Р' - 243: 32, # 'С' - 244: 40, # 'Т' - 245: 52, # 'У' - 246: 56, # 'Ж' - 247: 33, # 'В' - 248: 61, # 'Ь' - 249: 62, # 'Ы' - 250: 51, # 'З' - 251: 57, # 'Ш' - 252: 47, # 'Э' - 253: 63, # 'Щ' - 254: 50, # 'Ч' - 255: 70, # 'Ъ' -} - -KOI8_R_RUSSIAN_MODEL = SingleByteCharSetModel( - charset_name="KOI8-R", - language="Russian", - char_to_order_map=KOI8_R_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", -) - -MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 37, # 'А' - 129: 44, # 'Б' - 130: 33, # 'В' - 131: 46, # 'Г' - 132: 41, # 'Д' - 133: 48, # 'Е' - 134: 56, # 'Ж' - 135: 51, # 'З' - 136: 42, # 'И' - 137: 60, # 'Й' - 138: 36, # 'К' - 139: 49, # 'Л' - 140: 38, # 'М' - 141: 31, # 'Н' - 142: 34, # 'О' - 143: 35, # 'П' - 144: 45, # 'Р' - 145: 32, # 'С' - 146: 40, # 'Т' - 147: 52, # 'У' - 148: 53, # 'Ф' - 149: 55, # 'Х' - 150: 58, # 'Ц' - 151: 50, # 'Ч' - 152: 57, # 'Ш' - 153: 63, # 'Щ' - 154: 70, # 'Ъ' - 155: 62, # 'Ы' - 156: 61, # 'Ь' - 157: 47, # 'Э' - 158: 59, # 'Ю' - 159: 43, # 'Я' - 160: 191, # '†' - 161: 192, # '°' - 162: 193, # 'Ґ' - 163: 194, # '£' - 164: 195, # '§' - 165: 196, # '•' - 166: 197, # '¶' - 167: 198, # 'І' - 168: 199, # '®' - 169: 200, # '©' - 170: 201, # '™' - 171: 202, # 'Ђ' - 172: 203, # 'ђ' - 173: 204, # '≠' - 174: 205, # 'Ѓ' - 175: 206, # 'ѓ' - 176: 207, # '∞' - 177: 208, # '±' - 178: 209, # '≤' - 179: 210, # '≥' - 180: 211, # 'і' - 181: 212, # 'µ' - 182: 213, # 'ґ' - 183: 214, # 'Ј' - 184: 215, # 'Є' - 185: 216, # 'є' - 186: 217, # 'Ї' - 187: 218, # 'ї' - 188: 219, # 'Љ' - 189: 220, # 'љ' - 190: 221, # 'Њ' - 191: 222, # 'њ' - 192: 223, # 'ј' - 193: 224, # 'Ѕ' - 194: 225, # '¬' - 195: 226, # '√' - 196: 227, # 'ƒ' - 197: 228, # '≈' - 198: 229, # '∆' - 199: 230, # '«' - 200: 231, # '»' - 201: 232, # '…' - 202: 233, # '\xa0' - 203: 234, # 'Ћ' - 204: 235, # 'ћ' - 205: 236, # 'Ќ' - 206: 237, # 'ќ' - 207: 238, # 'ѕ' - 208: 239, # '–' - 209: 240, # '—' - 210: 241, # '“' - 211: 242, # '”' - 212: 243, # '‘' - 213: 244, # '’' - 214: 245, # '÷' - 215: 246, # '„' - 216: 247, # 'Ў' - 217: 248, # 'ў' - 218: 249, # 'Џ' - 219: 250, # 'џ' - 220: 251, # '№' - 221: 252, # 'Ё' - 222: 68, # 'ё' - 223: 16, # 'я' - 224: 3, # 'а' - 225: 21, # 'б' - 226: 10, # 'в' - 227: 19, # 'г' - 228: 13, # 'д' - 229: 2, # 'е' - 230: 24, # 'ж' - 231: 20, # 'з' - 232: 4, # 'и' - 233: 23, # 'й' - 234: 11, # 'к' - 235: 8, # 'л' - 236: 12, # 'м' - 237: 5, # 'н' - 238: 1, # 'о' - 239: 15, # 'п' - 240: 9, # 'р' - 241: 7, # 'с' - 242: 6, # 'т' - 243: 14, # 'у' - 244: 39, # 'ф' - 245: 26, # 'х' - 246: 28, # 'ц' - 247: 22, # 'ч' - 248: 25, # 'ш' - 249: 29, # 'щ' - 250: 54, # 'ъ' - 251: 18, # 'ы' - 252: 17, # 'ь' - 253: 30, # 'э' - 254: 27, # 'ю' - 255: 255, # '€' -} - -MACCYRILLIC_RUSSIAN_MODEL = SingleByteCharSetModel( - charset_name="MacCyrillic", - language="Russian", - char_to_order_map=MACCYRILLIC_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", -) - -ISO_8859_5_RUSSIAN_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 142, # 'A' - 66: 143, # 'B' - 67: 144, # 'C' - 68: 145, # 'D' - 69: 146, # 'E' - 70: 147, # 'F' - 71: 148, # 'G' - 72: 149, # 'H' - 73: 150, # 'I' - 74: 151, # 'J' - 75: 152, # 'K' - 76: 74, # 'L' - 77: 153, # 'M' - 78: 75, # 'N' - 79: 154, # 'O' - 80: 155, # 'P' - 81: 156, # 'Q' - 82: 157, # 'R' - 83: 158, # 'S' - 84: 159, # 'T' - 85: 160, # 'U' - 86: 161, # 'V' - 87: 162, # 'W' - 88: 163, # 'X' - 89: 164, # 'Y' - 90: 165, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 71, # 'a' - 98: 172, # 'b' - 99: 66, # 'c' - 100: 173, # 'd' - 101: 65, # 'e' - 102: 174, # 'f' - 103: 76, # 'g' - 104: 175, # 'h' - 105: 64, # 'i' - 106: 176, # 'j' - 107: 177, # 'k' - 108: 77, # 'l' - 109: 72, # 'm' - 110: 178, # 'n' - 111: 69, # 'o' - 112: 67, # 'p' - 113: 179, # 'q' - 114: 78, # 'r' - 115: 73, # 's' - 116: 180, # 't' - 117: 181, # 'u' - 118: 79, # 'v' - 119: 182, # 'w' - 120: 183, # 'x' - 121: 184, # 'y' - 122: 185, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 191, # '\x80' - 129: 192, # '\x81' - 130: 193, # '\x82' - 131: 194, # '\x83' - 132: 195, # '\x84' - 133: 196, # '\x85' - 134: 197, # '\x86' - 135: 198, # '\x87' - 136: 199, # '\x88' - 137: 200, # '\x89' - 138: 201, # '\x8a' - 139: 202, # '\x8b' - 140: 203, # '\x8c' - 141: 204, # '\x8d' - 142: 205, # '\x8e' - 143: 206, # '\x8f' - 144: 207, # '\x90' - 145: 208, # '\x91' - 146: 209, # '\x92' - 147: 210, # '\x93' - 148: 211, # '\x94' - 149: 212, # '\x95' - 150: 213, # '\x96' - 151: 214, # '\x97' - 152: 215, # '\x98' - 153: 216, # '\x99' - 154: 217, # '\x9a' - 155: 218, # '\x9b' - 156: 219, # '\x9c' - 157: 220, # '\x9d' - 158: 221, # '\x9e' - 159: 222, # '\x9f' - 160: 223, # '\xa0' - 161: 224, # 'Ё' - 162: 225, # 'Ђ' - 163: 226, # 'Ѓ' - 164: 227, # 'Є' - 165: 228, # 'Ѕ' - 166: 229, # 'І' - 167: 230, # 'Ї' - 168: 231, # 'Ј' - 169: 232, # 'Љ' - 170: 233, # 'Њ' - 171: 234, # 'Ћ' - 172: 235, # 'Ќ' - 173: 236, # '\xad' - 174: 237, # 'Ў' - 175: 238, # 'Џ' - 176: 37, # 'А' - 177: 44, # 'Б' - 178: 33, # 'В' - 179: 46, # 'Г' - 180: 41, # 'Д' - 181: 48, # 'Е' - 182: 56, # 'Ж' - 183: 51, # 'З' - 184: 42, # 'И' - 185: 60, # 'Й' - 186: 36, # 'К' - 187: 49, # 'Л' - 188: 38, # 'М' - 189: 31, # 'Н' - 190: 34, # 'О' - 191: 35, # 'П' - 192: 45, # 'Р' - 193: 32, # 'С' - 194: 40, # 'Т' - 195: 52, # 'У' - 196: 53, # 'Ф' - 197: 55, # 'Х' - 198: 58, # 'Ц' - 199: 50, # 'Ч' - 200: 57, # 'Ш' - 201: 63, # 'Щ' - 202: 70, # 'Ъ' - 203: 62, # 'Ы' - 204: 61, # 'Ь' - 205: 47, # 'Э' - 206: 59, # 'Ю' - 207: 43, # 'Я' - 208: 3, # 'а' - 209: 21, # 'б' - 210: 10, # 'в' - 211: 19, # 'г' - 212: 13, # 'д' - 213: 2, # 'е' - 214: 24, # 'ж' - 215: 20, # 'з' - 216: 4, # 'и' - 217: 23, # 'й' - 218: 11, # 'к' - 219: 8, # 'л' - 220: 12, # 'м' - 221: 5, # 'н' - 222: 1, # 'о' - 223: 15, # 'п' - 224: 9, # 'р' - 225: 7, # 'с' - 226: 6, # 'т' - 227: 14, # 'у' - 228: 39, # 'ф' - 229: 26, # 'х' - 230: 28, # 'ц' - 231: 22, # 'ч' - 232: 25, # 'ш' - 233: 29, # 'щ' - 234: 54, # 'ъ' - 235: 18, # 'ы' - 236: 17, # 'ь' - 237: 30, # 'э' - 238: 27, # 'ю' - 239: 16, # 'я' - 240: 239, # '№' - 241: 68, # 'ё' - 242: 240, # 'ђ' - 243: 241, # 'ѓ' - 244: 242, # 'є' - 245: 243, # 'ѕ' - 246: 244, # 'і' - 247: 245, # 'ї' - 248: 246, # 'ј' - 249: 247, # 'љ' - 250: 248, # 'њ' - 251: 249, # 'ћ' - 252: 250, # 'ќ' - 253: 251, # '§' - 254: 252, # 'ў' - 255: 255, # 'џ' -} - -ISO_8859_5_RUSSIAN_MODEL = SingleByteCharSetModel( - charset_name="ISO-8859-5", - language="Russian", - char_to_order_map=ISO_8859_5_RUSSIAN_CHAR_TO_ORDER, - language_model=RUSSIAN_LANG_MODEL, - typical_positive_ratio=0.976601, - keep_ascii_letters=False, - alphabet="ЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langthaimodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langthaimodel.py deleted file mode 100644 index 883fdb1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langthaimodel.py +++ /dev/null @@ -1,4380 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -THAI_LANG_MODEL = { - 5: { # 'ก' - 5: 2, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 2, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 3, # 'ฎ' - 57: 2, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 2, # 'ณ' - 20: 2, # 'ด' - 19: 3, # 'ต' - 44: 0, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 1, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 1, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 2, # 'ม' - 16: 1, # 'ย' - 2: 3, # 'ร' - 61: 2, # 'ฤ' - 15: 3, # 'ล' - 12: 3, # 'ว' - 42: 2, # 'ศ' - 46: 3, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 3, # 'อ' - 63: 1, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 3, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 0, # 'ึ' - 27: 2, # 'ื' - 32: 2, # 'ุ' - 35: 1, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 1, # 'ใ' - 33: 2, # 'ไ' - 50: 1, # 'ๆ' - 37: 3, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 30: { # 'ข' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 1, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 2, # 'ณ' - 20: 0, # 'ด' - 19: 2, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 1, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 2, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 1, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 2, # 'ี' - 40: 3, # 'ึ' - 27: 1, # 'ื' - 32: 1, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 2, # '่' - 7: 3, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 24: { # 'ค' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 2, # 'ค' - 8: 2, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 2, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 0, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 2, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 3, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 2, # 'า' - 36: 3, # 'ำ' - 23: 3, # 'ิ' - 13: 2, # 'ี' - 40: 0, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 2, # 'ู' - 11: 1, # 'เ' - 28: 0, # 'แ' - 41: 3, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 8: { # 'ง' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 3, # 'ค' - 8: 2, # 'ง' - 26: 2, # 'จ' - 52: 1, # 'ฉ' - 34: 2, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 3, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 1, # 'ฝ' - 31: 2, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 2, # 'ม' - 16: 1, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 2, # 'ว' - 42: 2, # 'ศ' - 46: 1, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 1, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 1, # 'ื' - 32: 1, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 3, # 'ๆ' - 37: 0, # '็' - 6: 2, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 26: { # 'จ' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 0, # 'ค' - 8: 2, # 'ง' - 26: 3, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 1, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 1, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 1, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 1, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 3, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 3, # 'ำ' - 23: 2, # 'ิ' - 13: 1, # 'ี' - 40: 3, # 'ึ' - 27: 1, # 'ื' - 32: 3, # 'ุ' - 35: 2, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 2, # '่' - 7: 2, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 52: { # 'ฉ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 3, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 3, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 1, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 1, # 'ั' - 1: 1, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 1, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 34: { # 'ช' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 1, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 1, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 1, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 2, # 'ั' - 1: 3, # 'า' - 36: 1, # 'ำ' - 23: 3, # 'ิ' - 13: 2, # 'ี' - 40: 0, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 1, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 51: { # 'ซ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 1, # 'ั' - 1: 1, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 2, # 'ี' - 40: 3, # 'ึ' - 27: 2, # 'ื' - 32: 1, # 'ุ' - 35: 1, # 'ู' - 11: 1, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 1, # '่' - 7: 2, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 47: { # 'ญ' - 5: 1, # 'ก' - 30: 1, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 3, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 1, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 2, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 2, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 0, # 'ไ' - 50: 1, # 'ๆ' - 37: 0, # '็' - 6: 2, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 58: { # 'ฎ' - 5: 2, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 1, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 2, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 57: { # 'ฏ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 3, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 49: { # 'ฐ' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 2, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 53: { # 'ฑ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 55: { # 'ฒ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 43: { # 'ณ' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 3, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 3, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 1, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 3, # 'ะ' - 10: 0, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 2, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 20: { # 'ด' - 5: 2, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 1, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 2, # 'ม' - 16: 3, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 3, # 'ั' - 1: 2, # 'า' - 36: 2, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 1, # 'ึ' - 27: 2, # 'ื' - 32: 3, # 'ุ' - 35: 2, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 2, # 'ๆ' - 37: 2, # '็' - 6: 1, # '่' - 7: 3, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 19: { # 'ต' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 1, # 'ต' - 44: 2, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 1, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 2, # 'ภ' - 9: 1, # 'ม' - 16: 1, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 3, # 'ส' - 21: 0, # 'ห' - 4: 3, # 'อ' - 63: 1, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 2, # 'ำ' - 23: 3, # 'ิ' - 13: 2, # 'ี' - 40: 1, # 'ึ' - 27: 1, # 'ื' - 32: 3, # 'ุ' - 35: 2, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 1, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 2, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 44: { # 'ถ' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 2, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 2, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 1, # 'ี' - 40: 3, # 'ึ' - 27: 2, # 'ื' - 32: 2, # 'ุ' - 35: 3, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 2, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 14: { # 'ท' - 5: 1, # 'ก' - 30: 1, # 'ข' - 24: 3, # 'ค' - 8: 1, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 3, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 2, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 3, # 'ย' - 2: 3, # 'ร' - 61: 1, # 'ฤ' - 15: 1, # 'ล' - 12: 2, # 'ว' - 42: 3, # 'ศ' - 46: 1, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 3, # 'ำ' - 23: 2, # 'ิ' - 13: 3, # 'ี' - 40: 2, # 'ึ' - 27: 1, # 'ื' - 32: 3, # 'ุ' - 35: 1, # 'ู' - 11: 0, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 48: { # 'ธ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 1, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 2, # 'า' - 36: 0, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 2, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 3: { # 'น' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 3, # 'ค' - 8: 1, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 1, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 2, # 'ถ' - 14: 3, # 'ท' - 48: 3, # 'ธ' - 3: 2, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 0, # 'ฝ' - 31: 2, # 'พ' - 54: 1, # 'ฟ' - 45: 1, # 'ภ' - 9: 2, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 1, # 'ฤ' - 15: 2, # 'ล' - 12: 3, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 3, # 'อ' - 63: 1, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 3, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 3, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 2, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 3, # 'โ' - 29: 3, # 'ใ' - 33: 3, # 'ไ' - 50: 2, # 'ๆ' - 37: 1, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 17: { # 'บ' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 1, # 'ง' - 26: 1, # 'จ' - 52: 1, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 3, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 1, # 'ฟ' - 45: 1, # 'ภ' - 9: 1, # 'ม' - 16: 0, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 3, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 2, # 'อ' - 63: 1, # 'ฯ' - 22: 0, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 2, # 'ำ' - 23: 2, # 'ิ' - 13: 2, # 'ี' - 40: 0, # 'ึ' - 27: 2, # 'ื' - 32: 3, # 'ุ' - 35: 2, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 2, # '่' - 7: 2, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 25: { # 'ป' - 5: 2, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 1, # 'ฎ' - 57: 3, # 'ฏ' - 49: 1, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 1, # 'ต' - 44: 1, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 0, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 1, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 0, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 1, # 'ษ' - 18: 2, # 'ส' - 21: 1, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 3, # 'ั' - 1: 1, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 3, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 1, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 2, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 2, # 'ไ' - 50: 0, # 'ๆ' - 37: 3, # '็' - 6: 1, # '่' - 7: 2, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 39: { # 'ผ' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 1, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 2, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 1, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 1, # 'ื' - 32: 0, # 'ุ' - 35: 3, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 1, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 62: { # 'ฝ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 1, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 1, # 'ี' - 40: 2, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 2, # '่' - 7: 1, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 31: { # 'พ' - 5: 1, # 'ก' - 30: 1, # 'ข' - 24: 1, # 'ค' - 8: 1, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 1, # 'ณ' - 20: 1, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 2, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 0, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 2, # 'ย' - 2: 3, # 'ร' - 61: 2, # 'ฤ' - 15: 2, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 1, # 'ห' - 4: 2, # 'อ' - 63: 1, # 'ฯ' - 22: 0, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 3, # 'ิ' - 13: 2, # 'ี' - 40: 1, # 'ึ' - 27: 3, # 'ื' - 32: 1, # 'ุ' - 35: 2, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 1, # '็' - 6: 0, # '่' - 7: 1, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 54: { # 'ฟ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 2, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 2, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 1, # 'ื' - 32: 1, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 2, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 45: { # 'ภ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 3, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 2, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 9: { # 'ม' - 5: 2, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 2, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 1, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 3, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 2, # 'ม' - 16: 1, # 'ย' - 2: 2, # 'ร' - 61: 2, # 'ฤ' - 15: 2, # 'ล' - 12: 2, # 'ว' - 42: 1, # 'ศ' - 46: 1, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 0, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 3, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 2, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 1, # 'ๆ' - 37: 1, # '็' - 6: 3, # '่' - 7: 2, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 16: { # 'ย' - 5: 3, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 2, # 'ช' - 51: 0, # 'ซ' - 47: 2, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 2, # 'ม' - 16: 0, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 3, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 1, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 3, # 'ี' - 40: 1, # 'ึ' - 27: 2, # 'ื' - 32: 2, # 'ุ' - 35: 3, # 'ู' - 11: 2, # 'เ' - 28: 1, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 2, # 'ๆ' - 37: 1, # '็' - 6: 3, # '่' - 7: 2, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 2: { # 'ร' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 2, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 3, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 3, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 3, # 'ถ' - 14: 3, # 'ท' - 48: 1, # 'ธ' - 3: 2, # 'น' - 17: 2, # 'บ' - 25: 3, # 'ป' - 39: 2, # 'ผ' - 62: 1, # 'ฝ' - 31: 2, # 'พ' - 54: 1, # 'ฟ' - 45: 1, # 'ภ' - 9: 3, # 'ม' - 16: 2, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 3, # 'ว' - 42: 2, # 'ศ' - 46: 2, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 3, # 'อ' - 63: 1, # 'ฯ' - 22: 3, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 2, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 3, # 'ู' - 11: 3, # 'เ' - 28: 3, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 3, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 3, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 61: { # 'ฤ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 2, # 'ต' - 44: 0, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 2, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 15: { # 'ล' - 5: 2, # 'ก' - 30: 3, # 'ข' - 24: 1, # 'ค' - 8: 3, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 1, # 'ม' - 16: 3, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 1, # 'ห' - 4: 3, # 'อ' - 63: 2, # 'ฯ' - 22: 3, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 2, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 2, # 'ึ' - 27: 3, # 'ื' - 32: 2, # 'ุ' - 35: 3, # 'ู' - 11: 2, # 'เ' - 28: 1, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 2, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 12: { # 'ว' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 1, # 'ค' - 8: 3, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 1, # 'ณ' - 20: 2, # 'ด' - 19: 1, # 'ต' - 44: 1, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 1, # 'ฟ' - 45: 0, # 'ภ' - 9: 3, # 'ม' - 16: 3, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 3, # 'ิ' - 13: 2, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 2, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 1, # 'ใ' - 33: 2, # 'ไ' - 50: 1, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 42: { # 'ศ' - 5: 1, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 1, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 2, # 'ว' - 42: 1, # 'ศ' - 46: 2, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 2, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 2, # 'ิ' - 13: 0, # 'ี' - 40: 3, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 2, # 'ู' - 11: 0, # 'เ' - 28: 1, # 'แ' - 41: 0, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 46: { # 'ษ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 2, # 'ฎ' - 57: 1, # 'ฏ' - 49: 2, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 3, # 'ณ' - 20: 0, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 1, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 2, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 18: { # 'ส' - 5: 2, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 2, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 3, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 2, # 'ภ' - 9: 3, # 'ม' - 16: 1, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 2, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 3, # 'ำ' - 23: 3, # 'ิ' - 13: 3, # 'ี' - 40: 2, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 3, # 'ู' - 11: 2, # 'เ' - 28: 0, # 'แ' - 41: 1, # 'โ' - 29: 0, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 1, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 21: { # 'ห' - 5: 3, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 1, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 2, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 3, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 0, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 3, # 'ม' - 16: 2, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 0, # 'ำ' - 23: 1, # 'ิ' - 13: 1, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 1, # 'ุ' - 35: 1, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 3, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 4: { # 'อ' - 5: 3, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 1, # 'ฟ' - 45: 1, # 'ภ' - 9: 3, # 'ม' - 16: 3, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 2, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 2, # 'ะ' - 10: 3, # 'ั' - 1: 3, # 'า' - 36: 2, # 'ำ' - 23: 2, # 'ิ' - 13: 3, # 'ี' - 40: 0, # 'ึ' - 27: 3, # 'ื' - 32: 3, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 1, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 1, # 'ๆ' - 37: 1, # '็' - 6: 2, # '่' - 7: 2, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 63: { # 'ฯ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 22: { # 'ะ' - 5: 3, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 1, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 3, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 1, # 'ถ' - 14: 3, # 'ท' - 48: 1, # 'ธ' - 3: 2, # 'น' - 17: 3, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 2, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 3, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 2, # 'อ' - 63: 1, # 'ฯ' - 22: 1, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 10: { # 'ั' - 5: 3, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 3, # 'ง' - 26: 3, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 3, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 2, # 'ฐ' - 53: 0, # 'ฑ' - 55: 3, # 'ฒ' - 43: 3, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 0, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 2, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 3, # 'ม' - 16: 3, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 3, # 'ว' - 42: 2, # 'ศ' - 46: 0, # 'ษ' - 18: 3, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 1: { # 'า' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 3, # 'ค' - 8: 3, # 'ง' - 26: 3, # 'จ' - 52: 0, # 'ฉ' - 34: 3, # 'ช' - 51: 1, # 'ซ' - 47: 2, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 3, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 1, # 'ถ' - 14: 3, # 'ท' - 48: 2, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 1, # 'ฝ' - 31: 3, # 'พ' - 54: 1, # 'ฟ' - 45: 1, # 'ภ' - 9: 3, # 'ม' - 16: 3, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 3, # 'ว' - 42: 2, # 'ศ' - 46: 3, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 2, # 'อ' - 63: 1, # 'ฯ' - 22: 3, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 1, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 36: { # 'ำ' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 3, # 'ค' - 8: 2, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 1, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 1, # 'ต' - 44: 1, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 1, # 'บ' - 25: 1, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 1, # 'ม' - 16: 0, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 3, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 23: { # 'ิ' - 5: 3, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 3, # 'จ' - 52: 0, # 'ฉ' - 34: 3, # 'ช' - 51: 0, # 'ซ' - 47: 2, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 1, # 'ถ' - 14: 3, # 'ท' - 48: 3, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 0, # 'ฝ' - 31: 3, # 'พ' - 54: 1, # 'ฟ' - 45: 2, # 'ภ' - 9: 3, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 3, # 'ว' - 42: 3, # 'ศ' - 46: 2, # 'ษ' - 18: 2, # 'ส' - 21: 3, # 'ห' - 4: 1, # 'อ' - 63: 1, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 1, # 'แ' - 41: 1, # 'โ' - 29: 1, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 2, # '้' - 38: 2, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 13: { # 'ี' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 1, # 'ผ' - 62: 0, # 'ฝ' - 31: 2, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 3, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 2, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 1, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 1, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 40: { # 'ึ' - 5: 3, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 3, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 1, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 27: { # 'ื' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 3, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 32: { # 'ุ' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 3, # 'ค' - 8: 3, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 2, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 1, # 'ฒ' - 43: 3, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 1, # 'ธ' - 3: 2, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 1, # 'ภ' - 9: 3, # 'ม' - 16: 1, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 1, # 'ว' - 42: 1, # 'ศ' - 46: 2, # 'ษ' - 18: 1, # 'ส' - 21: 1, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 0, # 'แ' - 41: 1, # 'โ' - 29: 0, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 2, # '้' - 38: 1, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 35: { # 'ู' - 5: 3, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 2, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 2, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 1, # 'ณ' - 20: 2, # 'ด' - 19: 2, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 2, # 'น' - 17: 0, # 'บ' - 25: 3, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 1, # 'แ' - 41: 1, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 3, # '่' - 7: 3, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 11: { # 'เ' - 5: 3, # 'ก' - 30: 3, # 'ข' - 24: 3, # 'ค' - 8: 2, # 'ง' - 26: 3, # 'จ' - 52: 3, # 'ฉ' - 34: 3, # 'ช' - 51: 2, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 1, # 'ณ' - 20: 3, # 'ด' - 19: 3, # 'ต' - 44: 1, # 'ถ' - 14: 3, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 3, # 'ป' - 39: 2, # 'ผ' - 62: 1, # 'ฝ' - 31: 3, # 'พ' - 54: 1, # 'ฟ' - 45: 3, # 'ภ' - 9: 3, # 'ม' - 16: 2, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 3, # 'ว' - 42: 2, # 'ศ' - 46: 0, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 28: { # 'แ' - 5: 3, # 'ก' - 30: 2, # 'ข' - 24: 2, # 'ค' - 8: 1, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 3, # 'ต' - 44: 2, # 'ถ' - 14: 3, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 2, # 'ป' - 39: 3, # 'ผ' - 62: 0, # 'ฝ' - 31: 2, # 'พ' - 54: 2, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 41: { # 'โ' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 1, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 2, # 'ต' - 44: 0, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 1, # 'บ' - 25: 3, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 1, # 'ฟ' - 45: 1, # 'ภ' - 9: 1, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 3, # 'ล' - 12: 0, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 0, # 'ห' - 4: 2, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 29: { # 'ใ' - 5: 2, # 'ก' - 30: 0, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 3, # 'จ' - 52: 0, # 'ฉ' - 34: 3, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 1, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 3, # 'ส' - 21: 3, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 33: { # 'ไ' - 5: 1, # 'ก' - 30: 2, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 3, # 'ด' - 19: 1, # 'ต' - 44: 0, # 'ถ' - 14: 3, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 1, # 'บ' - 25: 3, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 2, # 'ฟ' - 45: 0, # 'ภ' - 9: 3, # 'ม' - 16: 0, # 'ย' - 2: 3, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 3, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 2, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 50: { # 'ๆ' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 37: { # '็' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 2, # 'ง' - 26: 3, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 1, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 2, # 'ต' - 44: 0, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 3, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 1, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 2, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 0, # 'ห' - 4: 1, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 1, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 6: { # '่' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 1, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 1, # 'ธ' - 3: 3, # 'น' - 17: 1, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 1, # 'ฝ' - 31: 1, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 3, # 'ม' - 16: 3, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 2, # 'ล' - 12: 3, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 1, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 1, # 'ะ' - 10: 0, # 'ั' - 1: 3, # 'า' - 36: 2, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 3, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 1, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 7: { # '้' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 2, # 'ค' - 8: 3, # 'ง' - 26: 2, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 1, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 1, # 'ด' - 19: 2, # 'ต' - 44: 1, # 'ถ' - 14: 2, # 'ท' - 48: 0, # 'ธ' - 3: 3, # 'น' - 17: 2, # 'บ' - 25: 2, # 'ป' - 39: 2, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 1, # 'ฟ' - 45: 0, # 'ภ' - 9: 3, # 'ม' - 16: 2, # 'ย' - 2: 2, # 'ร' - 61: 0, # 'ฤ' - 15: 1, # 'ล' - 12: 3, # 'ว' - 42: 1, # 'ศ' - 46: 0, # 'ษ' - 18: 2, # 'ส' - 21: 2, # 'ห' - 4: 3, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 3, # 'า' - 36: 2, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 2, # 'ใ' - 33: 2, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 38: { # '์' - 5: 2, # 'ก' - 30: 1, # 'ข' - 24: 1, # 'ค' - 8: 0, # 'ง' - 26: 1, # 'จ' - 52: 0, # 'ฉ' - 34: 1, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 2, # 'ด' - 19: 1, # 'ต' - 44: 1, # 'ถ' - 14: 1, # 'ท' - 48: 0, # 'ธ' - 3: 1, # 'น' - 17: 1, # 'บ' - 25: 1, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 1, # 'พ' - 54: 1, # 'ฟ' - 45: 0, # 'ภ' - 9: 2, # 'ม' - 16: 0, # 'ย' - 2: 1, # 'ร' - 61: 1, # 'ฤ' - 15: 1, # 'ล' - 12: 1, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 1, # 'ส' - 21: 1, # 'ห' - 4: 2, # 'อ' - 63: 1, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 2, # 'เ' - 28: 2, # 'แ' - 41: 1, # 'โ' - 29: 1, # 'ใ' - 33: 1, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 0, # '๑' - 59: 0, # '๒' - 60: 0, # '๕' - }, - 56: { # '๑' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 2, # '๑' - 59: 1, # '๒' - 60: 1, # '๕' - }, - 59: { # '๒' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 1, # '๑' - 59: 1, # '๒' - 60: 3, # '๕' - }, - 60: { # '๕' - 5: 0, # 'ก' - 30: 0, # 'ข' - 24: 0, # 'ค' - 8: 0, # 'ง' - 26: 0, # 'จ' - 52: 0, # 'ฉ' - 34: 0, # 'ช' - 51: 0, # 'ซ' - 47: 0, # 'ญ' - 58: 0, # 'ฎ' - 57: 0, # 'ฏ' - 49: 0, # 'ฐ' - 53: 0, # 'ฑ' - 55: 0, # 'ฒ' - 43: 0, # 'ณ' - 20: 0, # 'ด' - 19: 0, # 'ต' - 44: 0, # 'ถ' - 14: 0, # 'ท' - 48: 0, # 'ธ' - 3: 0, # 'น' - 17: 0, # 'บ' - 25: 0, # 'ป' - 39: 0, # 'ผ' - 62: 0, # 'ฝ' - 31: 0, # 'พ' - 54: 0, # 'ฟ' - 45: 0, # 'ภ' - 9: 0, # 'ม' - 16: 0, # 'ย' - 2: 0, # 'ร' - 61: 0, # 'ฤ' - 15: 0, # 'ล' - 12: 0, # 'ว' - 42: 0, # 'ศ' - 46: 0, # 'ษ' - 18: 0, # 'ส' - 21: 0, # 'ห' - 4: 0, # 'อ' - 63: 0, # 'ฯ' - 22: 0, # 'ะ' - 10: 0, # 'ั' - 1: 0, # 'า' - 36: 0, # 'ำ' - 23: 0, # 'ิ' - 13: 0, # 'ี' - 40: 0, # 'ึ' - 27: 0, # 'ื' - 32: 0, # 'ุ' - 35: 0, # 'ู' - 11: 0, # 'เ' - 28: 0, # 'แ' - 41: 0, # 'โ' - 29: 0, # 'ใ' - 33: 0, # 'ไ' - 50: 0, # 'ๆ' - 37: 0, # '็' - 6: 0, # '่' - 7: 0, # '้' - 38: 0, # '์' - 56: 2, # '๑' - 59: 1, # '๒' - 60: 0, # '๕' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -TIS_620_THAI_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 254, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 254, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 253, # ' ' - 33: 253, # '!' - 34: 253, # '"' - 35: 253, # '#' - 36: 253, # '$' - 37: 253, # '%' - 38: 253, # '&' - 39: 253, # "'" - 40: 253, # '(' - 41: 253, # ')' - 42: 253, # '*' - 43: 253, # '+' - 44: 253, # ',' - 45: 253, # '-' - 46: 253, # '.' - 47: 253, # '/' - 48: 252, # '0' - 49: 252, # '1' - 50: 252, # '2' - 51: 252, # '3' - 52: 252, # '4' - 53: 252, # '5' - 54: 252, # '6' - 55: 252, # '7' - 56: 252, # '8' - 57: 252, # '9' - 58: 253, # ':' - 59: 253, # ';' - 60: 253, # '<' - 61: 253, # '=' - 62: 253, # '>' - 63: 253, # '?' - 64: 253, # '@' - 65: 182, # 'A' - 66: 106, # 'B' - 67: 107, # 'C' - 68: 100, # 'D' - 69: 183, # 'E' - 70: 184, # 'F' - 71: 185, # 'G' - 72: 101, # 'H' - 73: 94, # 'I' - 74: 186, # 'J' - 75: 187, # 'K' - 76: 108, # 'L' - 77: 109, # 'M' - 78: 110, # 'N' - 79: 111, # 'O' - 80: 188, # 'P' - 81: 189, # 'Q' - 82: 190, # 'R' - 83: 89, # 'S' - 84: 95, # 'T' - 85: 112, # 'U' - 86: 113, # 'V' - 87: 191, # 'W' - 88: 192, # 'X' - 89: 193, # 'Y' - 90: 194, # 'Z' - 91: 253, # '[' - 92: 253, # '\\' - 93: 253, # ']' - 94: 253, # '^' - 95: 253, # '_' - 96: 253, # '`' - 97: 64, # 'a' - 98: 72, # 'b' - 99: 73, # 'c' - 100: 114, # 'd' - 101: 74, # 'e' - 102: 115, # 'f' - 103: 116, # 'g' - 104: 102, # 'h' - 105: 81, # 'i' - 106: 201, # 'j' - 107: 117, # 'k' - 108: 90, # 'l' - 109: 103, # 'm' - 110: 78, # 'n' - 111: 82, # 'o' - 112: 96, # 'p' - 113: 202, # 'q' - 114: 91, # 'r' - 115: 79, # 's' - 116: 84, # 't' - 117: 104, # 'u' - 118: 105, # 'v' - 119: 97, # 'w' - 120: 98, # 'x' - 121: 92, # 'y' - 122: 203, # 'z' - 123: 253, # '{' - 124: 253, # '|' - 125: 253, # '}' - 126: 253, # '~' - 127: 253, # '\x7f' - 128: 209, # '\x80' - 129: 210, # '\x81' - 130: 211, # '\x82' - 131: 212, # '\x83' - 132: 213, # '\x84' - 133: 88, # '\x85' - 134: 214, # '\x86' - 135: 215, # '\x87' - 136: 216, # '\x88' - 137: 217, # '\x89' - 138: 218, # '\x8a' - 139: 219, # '\x8b' - 140: 220, # '\x8c' - 141: 118, # '\x8d' - 142: 221, # '\x8e' - 143: 222, # '\x8f' - 144: 223, # '\x90' - 145: 224, # '\x91' - 146: 99, # '\x92' - 147: 85, # '\x93' - 148: 83, # '\x94' - 149: 225, # '\x95' - 150: 226, # '\x96' - 151: 227, # '\x97' - 152: 228, # '\x98' - 153: 229, # '\x99' - 154: 230, # '\x9a' - 155: 231, # '\x9b' - 156: 232, # '\x9c' - 157: 233, # '\x9d' - 158: 234, # '\x9e' - 159: 235, # '\x9f' - 160: 236, # None - 161: 5, # 'ก' - 162: 30, # 'ข' - 163: 237, # 'ฃ' - 164: 24, # 'ค' - 165: 238, # 'ฅ' - 166: 75, # 'ฆ' - 167: 8, # 'ง' - 168: 26, # 'จ' - 169: 52, # 'ฉ' - 170: 34, # 'ช' - 171: 51, # 'ซ' - 172: 119, # 'ฌ' - 173: 47, # 'ญ' - 174: 58, # 'ฎ' - 175: 57, # 'ฏ' - 176: 49, # 'ฐ' - 177: 53, # 'ฑ' - 178: 55, # 'ฒ' - 179: 43, # 'ณ' - 180: 20, # 'ด' - 181: 19, # 'ต' - 182: 44, # 'ถ' - 183: 14, # 'ท' - 184: 48, # 'ธ' - 185: 3, # 'น' - 186: 17, # 'บ' - 187: 25, # 'ป' - 188: 39, # 'ผ' - 189: 62, # 'ฝ' - 190: 31, # 'พ' - 191: 54, # 'ฟ' - 192: 45, # 'ภ' - 193: 9, # 'ม' - 194: 16, # 'ย' - 195: 2, # 'ร' - 196: 61, # 'ฤ' - 197: 15, # 'ล' - 198: 239, # 'ฦ' - 199: 12, # 'ว' - 200: 42, # 'ศ' - 201: 46, # 'ษ' - 202: 18, # 'ส' - 203: 21, # 'ห' - 204: 76, # 'ฬ' - 205: 4, # 'อ' - 206: 66, # 'ฮ' - 207: 63, # 'ฯ' - 208: 22, # 'ะ' - 209: 10, # 'ั' - 210: 1, # 'า' - 211: 36, # 'ำ' - 212: 23, # 'ิ' - 213: 13, # 'ี' - 214: 40, # 'ึ' - 215: 27, # 'ื' - 216: 32, # 'ุ' - 217: 35, # 'ู' - 218: 86, # 'ฺ' - 219: 240, # None - 220: 241, # None - 221: 242, # None - 222: 243, # None - 223: 244, # '฿' - 224: 11, # 'เ' - 225: 28, # 'แ' - 226: 41, # 'โ' - 227: 29, # 'ใ' - 228: 33, # 'ไ' - 229: 245, # 'ๅ' - 230: 50, # 'ๆ' - 231: 37, # '็' - 232: 6, # '่' - 233: 7, # '้' - 234: 67, # '๊' - 235: 77, # '๋' - 236: 38, # '์' - 237: 93, # 'ํ' - 238: 246, # '๎' - 239: 247, # '๏' - 240: 68, # '๐' - 241: 56, # '๑' - 242: 59, # '๒' - 243: 65, # '๓' - 244: 69, # '๔' - 245: 60, # '๕' - 246: 70, # '๖' - 247: 80, # '๗' - 248: 71, # '๘' - 249: 87, # '๙' - 250: 248, # '๚' - 251: 249, # '๛' - 252: 250, # None - 253: 251, # None - 254: 252, # None - 255: 253, # None -} - -TIS_620_THAI_MODEL = SingleByteCharSetModel( - charset_name="TIS-620", - language="Thai", - char_to_order_map=TIS_620_THAI_CHAR_TO_ORDER, - language_model=THAI_LANG_MODEL, - typical_positive_ratio=0.926386, - keep_ascii_letters=False, - alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langturkishmodel.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langturkishmodel.py deleted file mode 100644 index 64c9433..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/langturkishmodel.py +++ /dev/null @@ -1,4380 +0,0 @@ -from chardet.sbcharsetprober import SingleByteCharSetModel - -# 3: Positive -# 2: Likely -# 1: Unlikely -# 0: Negative - -TURKISH_LANG_MODEL = { - 23: { # 'A' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 1, # 'h' - 3: 1, # 'i' - 24: 0, # 'j' - 10: 2, # 'k' - 5: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 1, # 'r' - 8: 1, # 's' - 9: 1, # 't' - 14: 1, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 37: { # 'B' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 2, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 1, # 'Ş' - 19: 1, # 'ş' - }, - 47: { # 'C' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 1, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 1, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 2, # 'j' - 10: 1, # 'k' - 5: 2, # 'l' - 13: 2, # 'm' - 4: 2, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 2, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 1, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 39: { # 'D' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 1, # 'l' - 13: 3, # 'm' - 4: 0, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 1, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 1, # 'Ş' - 19: 0, # 'ş' - }, - 29: { # 'E' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 1, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 0, # 'h' - 3: 1, # 'i' - 24: 1, # 'j' - 10: 0, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 1, # 's' - 9: 1, # 't' - 14: 1, # 'u' - 32: 1, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 52: { # 'F' - 23: 0, # 'A' - 37: 1, # 'B' - 47: 1, # 'C' - 39: 1, # 'D' - 29: 1, # 'E' - 52: 2, # 'F' - 36: 0, # 'G' - 45: 2, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 1, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 1, # 'b' - 28: 1, # 'c' - 12: 1, # 'd' - 2: 0, # 'e' - 18: 1, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 2, # 'i' - 24: 1, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 1, # 'm' - 4: 2, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 2, # 'r' - 8: 1, # 's' - 9: 1, # 't' - 14: 1, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 1, # 'Ö' - 55: 2, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 2, # 'ş' - }, - 36: { # 'G' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 2, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 2, # 'N' - 42: 1, # 'O' - 48: 1, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 1, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 1, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 0, # 'r' - 8: 1, # 's' - 9: 1, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 1, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 2, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 45: { # 'H' - 23: 0, # 'A' - 37: 1, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 2, # 'G' - 45: 1, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 1, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 2, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 2, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 1, # 'o' - 26: 1, # 'p' - 7: 1, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 2, # 'ğ' - 41: 1, # 'İ' - 6: 0, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 53: { # 'I' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 2, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 1, # 'Ş' - 19: 1, # 'ş' - }, - 60: { # 'J' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 1, # 'd' - 2: 0, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 1, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 1, # 's' - 9: 0, # 't' - 14: 0, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 16: { # 'K' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 1, # 'e' - 18: 3, # 'f' - 27: 3, # 'g' - 25: 3, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 0, # 'u' - 32: 3, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 1, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 49: { # 'L' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 2, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 2, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 0, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 2, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 2, # 'n' - 15: 1, # 'o' - 26: 1, # 'p' - 7: 1, # 'r' - 8: 1, # 's' - 9: 1, # 't' - 14: 0, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 2, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 1, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 20: { # 'M' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 1, # 'h' - 3: 2, # 'i' - 24: 2, # 'j' - 10: 2, # 'k' - 5: 2, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 3, # 'r' - 8: 0, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 46: { # 'N' - 23: 0, # 'A' - 37: 1, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 2, # 'j' - 10: 1, # 'k' - 5: 1, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 1, # 'o' - 26: 1, # 'p' - 7: 1, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 1, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 1, # 'İ' - 6: 2, # 'ı' - 40: 1, # 'Ş' - 19: 1, # 'ş' - }, - 42: { # 'O' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 1, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 0, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 2, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 2, # 'İ' - 6: 1, # 'ı' - 40: 1, # 'Ş' - 19: 1, # 'ş' - }, - 48: { # 'P' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 2, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 2, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 2, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 0, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 44: { # 'R' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 1, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 2, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 1, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 1, # 'Ş' - 19: 1, # 'ş' - }, - 35: { # 'S' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 1, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 1, # 'l' - 13: 2, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 1, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 2, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 31: { # 'T' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 0, # 'c' - 12: 1, # 'd' - 2: 3, # 'e' - 18: 2, # 'f' - 27: 2, # 'g' - 25: 0, # 'h' - 3: 1, # 'i' - 24: 1, # 'j' - 10: 2, # 'k' - 5: 2, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 2, # 'r' - 8: 0, # 's' - 9: 2, # 't' - 14: 2, # 'u' - 32: 1, # 'v' - 57: 1, # 'w' - 58: 1, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 51: { # 'U' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 1, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 1, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 38: { # 'V' - 23: 1, # 'A' - 37: 1, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 1, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 2, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 2, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 1, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 1, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 3, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 62: { # 'W' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 0, # 'd' - 2: 0, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 0, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 0, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 43: { # 'Y' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 0, # 'G' - 45: 1, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 2, # 'N' - 42: 0, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 1, # 'j' - 10: 1, # 'k' - 5: 1, # 'l' - 13: 3, # 'm' - 4: 0, # 'n' - 15: 2, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 2, # 'Ö' - 55: 1, # 'Ü' - 59: 1, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 0, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 56: { # 'Z' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 2, # 'Z' - 1: 2, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 2, # 'i' - 24: 1, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 1, # 'r' - 8: 1, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 1: { # 'a' - 23: 3, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 1, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 3, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 2, # 'Z' - 1: 2, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 2, # 'e' - 18: 3, # 'f' - 27: 3, # 'g' - 25: 3, # 'h' - 3: 3, # 'i' - 24: 3, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 3, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 3, # 'v' - 57: 2, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 1, # 'î' - 34: 1, # 'ö' - 17: 3, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 21: { # 'b' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 3, # 'g' - 25: 1, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 3, # 'p' - 7: 1, # 'r' - 8: 2, # 's' - 9: 2, # 't' - 14: 2, # 'u' - 32: 1, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 28: { # 'c' - 23: 0, # 'A' - 37: 1, # 'B' - 47: 1, # 'C' - 39: 1, # 'D' - 29: 2, # 'E' - 52: 0, # 'F' - 36: 2, # 'G' - 45: 2, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 2, # 'T' - 51: 2, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 3, # 'Y' - 56: 0, # 'Z' - 1: 1, # 'a' - 21: 1, # 'b' - 28: 2, # 'c' - 12: 2, # 'd' - 2: 1, # 'e' - 18: 1, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 3, # 'i' - 24: 1, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 2, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 1, # 'u' - 32: 0, # 'v' - 57: 1, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 1, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 1, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 1, # 'î' - 34: 2, # 'ö' - 17: 2, # 'ü' - 30: 2, # 'ğ' - 41: 1, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 2, # 'ş' - }, - 12: { # 'd' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 2, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 1, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 1, # 'f' - 27: 3, # 'g' - 25: 3, # 'h' - 3: 2, # 'i' - 24: 3, # 'j' - 10: 2, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 2, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 1, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 3, # 'y' - 22: 1, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 2: { # 'e' - 23: 2, # 'A' - 37: 0, # 'B' - 47: 2, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 1, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 1, # 'R' - 35: 0, # 'S' - 31: 3, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 2, # 'e' - 18: 3, # 'f' - 27: 3, # 'g' - 25: 3, # 'h' - 3: 3, # 'i' - 24: 3, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 3, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 3, # 'v' - 57: 2, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 1, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 3, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 18: { # 'f' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 2, # 'f' - 27: 1, # 'g' - 25: 1, # 'h' - 3: 1, # 'i' - 24: 1, # 'j' - 10: 1, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 1, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 1, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 27: { # 'g' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 1, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 1, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 1, # 'h' - 3: 2, # 'i' - 24: 3, # 'j' - 10: 2, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 2, # 'r' - 8: 2, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 1, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 25: { # 'h' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 2, # 'h' - 3: 2, # 'i' - 24: 3, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 1, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 1, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 3: { # 'i' - 23: 2, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 0, # 'N' - 42: 1, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 1, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 2, # 'f' - 27: 3, # 'g' - 25: 1, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 3, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 1, # 'w' - 58: 1, # 'x' - 11: 3, # 'y' - 22: 1, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 1, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 3, # 'ü' - 30: 0, # 'ğ' - 41: 1, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 24: { # 'j' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 2, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 1, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 2, # 'f' - 27: 1, # 'g' - 25: 1, # 'h' - 3: 2, # 'i' - 24: 1, # 'j' - 10: 2, # 'k' - 5: 2, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 2, # 'r' - 8: 3, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 2, # 'x' - 11: 1, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 10: { # 'k' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 3, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 3, # 'e' - 18: 1, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 2, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 3, # 'p' - 7: 2, # 'r' - 8: 2, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 3, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 5: { # 'l' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 1, # 'e' - 18: 3, # 'f' - 27: 3, # 'g' - 25: 2, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 1, # 'l' - 13: 1, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 2, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 13: { # 'm' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 3, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 2, # 'e' - 18: 3, # 'f' - 27: 3, # 'g' - 25: 3, # 'h' - 3: 3, # 'i' - 24: 3, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 2, # 'u' - 32: 2, # 'v' - 57: 1, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 3, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 4: { # 'n' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 2, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 1, # 'f' - 27: 2, # 'g' - 25: 3, # 'h' - 3: 2, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 3, # 'p' - 7: 2, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 2, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 15: { # 'o' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 2, # 'L' - 20: 0, # 'M' - 46: 2, # 'N' - 42: 1, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 1, # 'i' - 24: 2, # 'j' - 10: 1, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 2, # 'o' - 26: 0, # 'p' - 7: 1, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 2, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 2, # 'ğ' - 41: 2, # 'İ' - 6: 3, # 'ı' - 40: 2, # 'Ş' - 19: 2, # 'ş' - }, - 26: { # 'p' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 1, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 1, # 'h' - 3: 2, # 'i' - 24: 3, # 'j' - 10: 1, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 2, # 'r' - 8: 1, # 's' - 9: 1, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 1, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 7: { # 'r' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 1, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 2, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 1, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 3, # 'h' - 3: 2, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 3, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 8: { # 's' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 2, # 'i' - 24: 3, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 3, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 2, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 9: { # 't' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 2, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 2, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 3, # 'v' - 57: 0, # 'w' - 58: 2, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 14: { # 'u' - 23: 3, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 2, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 3, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 2, # 'Z' - 1: 2, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 2, # 'e' - 18: 2, # 'f' - 27: 3, # 'g' - 25: 3, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 3, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 2, # 'v' - 57: 2, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 3, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 32: { # 'v' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 1, # 'j' - 10: 1, # 'k' - 5: 3, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 1, # 'r' - 8: 2, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 1, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 57: { # 'w' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 1, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 1, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 1, # 's' - 9: 0, # 't' - 14: 1, # 'u' - 32: 0, # 'v' - 57: 2, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 58: { # 'x' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 1, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 1, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 2, # 'i' - 24: 2, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 2, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 1, # 'r' - 8: 2, # 's' - 9: 1, # 't' - 14: 0, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 11: { # 'y' - 23: 1, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 2, # 'i' - 24: 1, # 'j' - 10: 2, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 2, # 'r' - 8: 1, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 1, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 3, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 22: { # 'z' - 23: 2, # 'A' - 37: 2, # 'B' - 47: 1, # 'C' - 39: 2, # 'D' - 29: 3, # 'E' - 52: 1, # 'F' - 36: 2, # 'G' - 45: 2, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 2, # 'N' - 42: 2, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 3, # 'T' - 51: 2, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 1, # 'Z' - 1: 1, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 2, # 'd' - 2: 2, # 'e' - 18: 3, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 2, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 0, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 3, # 'y' - 22: 2, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 2, # 'Ü' - 59: 1, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 2, # 'ü' - 30: 2, # 'ğ' - 41: 1, # 'İ' - 6: 3, # 'ı' - 40: 1, # 'Ş' - 19: 2, # 'ş' - }, - 63: { # '·' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 0, # 'd' - 2: 1, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 0, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 54: { # 'Ç' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 1, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 1, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 1, # 'b' - 28: 0, # 'c' - 12: 1, # 'd' - 2: 0, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 0, # 'h' - 3: 3, # 'i' - 24: 0, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 2, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 2, # 'r' - 8: 0, # 's' - 9: 1, # 't' - 14: 0, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 2, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 50: { # 'Ö' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 1, # 'D' - 29: 2, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 2, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 1, # 'N' - 42: 2, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 2, # 'd' - 2: 0, # 'e' - 18: 1, # 'f' - 27: 1, # 'g' - 25: 1, # 'h' - 3: 2, # 'i' - 24: 0, # 'j' - 10: 2, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 3, # 'n' - 15: 2, # 'o' - 26: 2, # 'p' - 7: 3, # 'r' - 8: 1, # 's' - 9: 2, # 't' - 14: 0, # 'u' - 32: 1, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 2, # 'ü' - 30: 1, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 55: { # 'Ü' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 1, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 1, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 1, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 1, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 1, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 0, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 59: { # 'â' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 0, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 2, # 'm' - 4: 0, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 2, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 1, # 'Ş' - 19: 0, # 'ş' - }, - 33: { # 'ç' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 3, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 0, # 'Z' - 1: 0, # 'a' - 21: 3, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 0, # 'e' - 18: 2, # 'f' - 27: 1, # 'g' - 25: 3, # 'h' - 3: 3, # 'i' - 24: 0, # 'j' - 10: 3, # 'k' - 5: 0, # 'l' - 13: 0, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 3, # 'r' - 8: 2, # 's' - 9: 3, # 't' - 14: 0, # 'u' - 32: 2, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 1, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 61: { # 'î' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 0, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 0, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 2, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 1, # 'j' - 10: 0, # 'k' - 5: 0, # 'l' - 13: 1, # 'm' - 4: 1, # 'n' - 15: 0, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 1, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 1, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 1, # 'î' - 34: 0, # 'ö' - 17: 0, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 1, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 34: { # 'ö' - 23: 0, # 'A' - 37: 1, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 1, # 'G' - 45: 1, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 1, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 2, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 1, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 2, # 'c' - 12: 1, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 2, # 'h' - 3: 1, # 'i' - 24: 2, # 'j' - 10: 1, # 'k' - 5: 2, # 'l' - 13: 3, # 'm' - 4: 2, # 'n' - 15: 2, # 'o' - 26: 0, # 'p' - 7: 0, # 'r' - 8: 3, # 's' - 9: 1, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 1, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 0, # 'ü' - 30: 2, # 'ğ' - 41: 1, # 'İ' - 6: 1, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 17: { # 'ü' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 0, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 1, # 'J' - 16: 1, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 0, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 0, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 0, # 'c' - 12: 1, # 'd' - 2: 3, # 'e' - 18: 1, # 'f' - 27: 2, # 'g' - 25: 0, # 'h' - 3: 1, # 'i' - 24: 1, # 'j' - 10: 2, # 'k' - 5: 3, # 'l' - 13: 2, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 2, # 'p' - 7: 2, # 'r' - 8: 3, # 's' - 9: 2, # 't' - 14: 3, # 'u' - 32: 1, # 'v' - 57: 1, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 2, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 30: { # 'ğ' - 23: 0, # 'A' - 37: 2, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 1, # 'M' - 46: 2, # 'N' - 42: 2, # 'O' - 48: 1, # 'P' - 44: 1, # 'R' - 35: 0, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 2, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 0, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 2, # 'e' - 18: 0, # 'f' - 27: 0, # 'g' - 25: 0, # 'h' - 3: 0, # 'i' - 24: 3, # 'j' - 10: 1, # 'k' - 5: 2, # 'l' - 13: 3, # 'm' - 4: 0, # 'n' - 15: 1, # 'o' - 26: 0, # 'p' - 7: 1, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 2, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 2, # 'İ' - 6: 2, # 'ı' - 40: 2, # 'Ş' - 19: 1, # 'ş' - }, - 41: { # 'İ' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 1, # 'D' - 29: 1, # 'E' - 52: 0, # 'F' - 36: 2, # 'G' - 45: 2, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 2, # 'P' - 44: 0, # 'R' - 35: 1, # 'S' - 31: 1, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 0, # 'Z' - 1: 1, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 2, # 'd' - 2: 1, # 'e' - 18: 0, # 'f' - 27: 3, # 'g' - 25: 2, # 'h' - 3: 2, # 'i' - 24: 2, # 'j' - 10: 2, # 'k' - 5: 0, # 'l' - 13: 1, # 'm' - 4: 3, # 'n' - 15: 1, # 'o' - 26: 1, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 2, # 't' - 14: 0, # 'u' - 32: 0, # 'v' - 57: 1, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 1, # 'Ü' - 59: 1, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 1, # 'ö' - 17: 1, # 'ü' - 30: 2, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 1, # 'ş' - }, - 6: { # 'ı' - 23: 2, # 'A' - 37: 0, # 'B' - 47: 0, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 2, # 'J' - 16: 3, # 'K' - 49: 0, # 'L' - 20: 3, # 'M' - 46: 1, # 'N' - 42: 0, # 'O' - 48: 0, # 'P' - 44: 0, # 'R' - 35: 0, # 'S' - 31: 2, # 'T' - 51: 0, # 'U' - 38: 0, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 1, # 'Z' - 1: 3, # 'a' - 21: 2, # 'b' - 28: 1, # 'c' - 12: 3, # 'd' - 2: 3, # 'e' - 18: 3, # 'f' - 27: 3, # 'g' - 25: 2, # 'h' - 3: 3, # 'i' - 24: 3, # 'j' - 10: 3, # 'k' - 5: 3, # 'l' - 13: 3, # 'm' - 4: 3, # 'n' - 15: 0, # 'o' - 26: 3, # 'p' - 7: 3, # 'r' - 8: 3, # 's' - 9: 3, # 't' - 14: 3, # 'u' - 32: 3, # 'v' - 57: 1, # 'w' - 58: 1, # 'x' - 11: 3, # 'y' - 22: 0, # 'z' - 63: 1, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 2, # 'ç' - 61: 0, # 'î' - 34: 0, # 'ö' - 17: 3, # 'ü' - 30: 0, # 'ğ' - 41: 0, # 'İ' - 6: 3, # 'ı' - 40: 0, # 'Ş' - 19: 0, # 'ş' - }, - 40: { # 'Ş' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 1, # 'D' - 29: 1, # 'E' - 52: 0, # 'F' - 36: 1, # 'G' - 45: 2, # 'H' - 53: 1, # 'I' - 60: 0, # 'J' - 16: 0, # 'K' - 49: 0, # 'L' - 20: 2, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 2, # 'P' - 44: 2, # 'R' - 35: 1, # 'S' - 31: 1, # 'T' - 51: 0, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 2, # 'Y' - 56: 1, # 'Z' - 1: 0, # 'a' - 21: 2, # 'b' - 28: 0, # 'c' - 12: 2, # 'd' - 2: 0, # 'e' - 18: 3, # 'f' - 27: 0, # 'g' - 25: 2, # 'h' - 3: 3, # 'i' - 24: 2, # 'j' - 10: 1, # 'k' - 5: 0, # 'l' - 13: 1, # 'm' - 4: 3, # 'n' - 15: 2, # 'o' - 26: 0, # 'p' - 7: 3, # 'r' - 8: 2, # 's' - 9: 2, # 't' - 14: 1, # 'u' - 32: 3, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 2, # 'y' - 22: 0, # 'z' - 63: 0, # '·' - 54: 0, # 'Ç' - 50: 0, # 'Ö' - 55: 1, # 'Ü' - 59: 0, # 'â' - 33: 0, # 'ç' - 61: 0, # 'î' - 34: 2, # 'ö' - 17: 1, # 'ü' - 30: 2, # 'ğ' - 41: 0, # 'İ' - 6: 2, # 'ı' - 40: 1, # 'Ş' - 19: 2, # 'ş' - }, - 19: { # 'ş' - 23: 0, # 'A' - 37: 0, # 'B' - 47: 1, # 'C' - 39: 0, # 'D' - 29: 0, # 'E' - 52: 2, # 'F' - 36: 1, # 'G' - 45: 0, # 'H' - 53: 0, # 'I' - 60: 0, # 'J' - 16: 3, # 'K' - 49: 2, # 'L' - 20: 0, # 'M' - 46: 1, # 'N' - 42: 1, # 'O' - 48: 1, # 'P' - 44: 1, # 'R' - 35: 1, # 'S' - 31: 0, # 'T' - 51: 1, # 'U' - 38: 1, # 'V' - 62: 0, # 'W' - 43: 1, # 'Y' - 56: 0, # 'Z' - 1: 3, # 'a' - 21: 1, # 'b' - 28: 2, # 'c' - 12: 0, # 'd' - 2: 3, # 'e' - 18: 0, # 'f' - 27: 2, # 'g' - 25: 1, # 'h' - 3: 1, # 'i' - 24: 0, # 'j' - 10: 2, # 'k' - 5: 2, # 'l' - 13: 3, # 'm' - 4: 0, # 'n' - 15: 0, # 'o' - 26: 1, # 'p' - 7: 3, # 'r' - 8: 0, # 's' - 9: 0, # 't' - 14: 3, # 'u' - 32: 0, # 'v' - 57: 0, # 'w' - 58: 0, # 'x' - 11: 0, # 'y' - 22: 2, # 'z' - 63: 0, # '·' - 54: 1, # 'Ç' - 50: 2, # 'Ö' - 55: 0, # 'Ü' - 59: 0, # 'â' - 33: 1, # 'ç' - 61: 1, # 'î' - 34: 2, # 'ö' - 17: 0, # 'ü' - 30: 1, # 'ğ' - 41: 1, # 'İ' - 6: 1, # 'ı' - 40: 1, # 'Ş' - 19: 1, # 'ş' - }, -} - -# 255: Undefined characters that did not exist in training text -# 254: Carriage/Return -# 253: symbol (punctuation) that does not belong to word -# 252: 0 - 9 -# 251: Control characters - -# Character Mapping Table(s): -ISO_8859_9_TURKISH_CHAR_TO_ORDER = { - 0: 255, # '\x00' - 1: 255, # '\x01' - 2: 255, # '\x02' - 3: 255, # '\x03' - 4: 255, # '\x04' - 5: 255, # '\x05' - 6: 255, # '\x06' - 7: 255, # '\x07' - 8: 255, # '\x08' - 9: 255, # '\t' - 10: 255, # '\n' - 11: 255, # '\x0b' - 12: 255, # '\x0c' - 13: 255, # '\r' - 14: 255, # '\x0e' - 15: 255, # '\x0f' - 16: 255, # '\x10' - 17: 255, # '\x11' - 18: 255, # '\x12' - 19: 255, # '\x13' - 20: 255, # '\x14' - 21: 255, # '\x15' - 22: 255, # '\x16' - 23: 255, # '\x17' - 24: 255, # '\x18' - 25: 255, # '\x19' - 26: 255, # '\x1a' - 27: 255, # '\x1b' - 28: 255, # '\x1c' - 29: 255, # '\x1d' - 30: 255, # '\x1e' - 31: 255, # '\x1f' - 32: 255, # ' ' - 33: 255, # '!' - 34: 255, # '"' - 35: 255, # '#' - 36: 255, # '$' - 37: 255, # '%' - 38: 255, # '&' - 39: 255, # "'" - 40: 255, # '(' - 41: 255, # ')' - 42: 255, # '*' - 43: 255, # '+' - 44: 255, # ',' - 45: 255, # '-' - 46: 255, # '.' - 47: 255, # '/' - 48: 255, # '0' - 49: 255, # '1' - 50: 255, # '2' - 51: 255, # '3' - 52: 255, # '4' - 53: 255, # '5' - 54: 255, # '6' - 55: 255, # '7' - 56: 255, # '8' - 57: 255, # '9' - 58: 255, # ':' - 59: 255, # ';' - 60: 255, # '<' - 61: 255, # '=' - 62: 255, # '>' - 63: 255, # '?' - 64: 255, # '@' - 65: 23, # 'A' - 66: 37, # 'B' - 67: 47, # 'C' - 68: 39, # 'D' - 69: 29, # 'E' - 70: 52, # 'F' - 71: 36, # 'G' - 72: 45, # 'H' - 73: 53, # 'I' - 74: 60, # 'J' - 75: 16, # 'K' - 76: 49, # 'L' - 77: 20, # 'M' - 78: 46, # 'N' - 79: 42, # 'O' - 80: 48, # 'P' - 81: 69, # 'Q' - 82: 44, # 'R' - 83: 35, # 'S' - 84: 31, # 'T' - 85: 51, # 'U' - 86: 38, # 'V' - 87: 62, # 'W' - 88: 65, # 'X' - 89: 43, # 'Y' - 90: 56, # 'Z' - 91: 255, # '[' - 92: 255, # '\\' - 93: 255, # ']' - 94: 255, # '^' - 95: 255, # '_' - 96: 255, # '`' - 97: 1, # 'a' - 98: 21, # 'b' - 99: 28, # 'c' - 100: 12, # 'd' - 101: 2, # 'e' - 102: 18, # 'f' - 103: 27, # 'g' - 104: 25, # 'h' - 105: 3, # 'i' - 106: 24, # 'j' - 107: 10, # 'k' - 108: 5, # 'l' - 109: 13, # 'm' - 110: 4, # 'n' - 111: 15, # 'o' - 112: 26, # 'p' - 113: 64, # 'q' - 114: 7, # 'r' - 115: 8, # 's' - 116: 9, # 't' - 117: 14, # 'u' - 118: 32, # 'v' - 119: 57, # 'w' - 120: 58, # 'x' - 121: 11, # 'y' - 122: 22, # 'z' - 123: 255, # '{' - 124: 255, # '|' - 125: 255, # '}' - 126: 255, # '~' - 127: 255, # '\x7f' - 128: 180, # '\x80' - 129: 179, # '\x81' - 130: 178, # '\x82' - 131: 177, # '\x83' - 132: 176, # '\x84' - 133: 175, # '\x85' - 134: 174, # '\x86' - 135: 173, # '\x87' - 136: 172, # '\x88' - 137: 171, # '\x89' - 138: 170, # '\x8a' - 139: 169, # '\x8b' - 140: 168, # '\x8c' - 141: 167, # '\x8d' - 142: 166, # '\x8e' - 143: 165, # '\x8f' - 144: 164, # '\x90' - 145: 163, # '\x91' - 146: 162, # '\x92' - 147: 161, # '\x93' - 148: 160, # '\x94' - 149: 159, # '\x95' - 150: 101, # '\x96' - 151: 158, # '\x97' - 152: 157, # '\x98' - 153: 156, # '\x99' - 154: 155, # '\x9a' - 155: 154, # '\x9b' - 156: 153, # '\x9c' - 157: 152, # '\x9d' - 158: 151, # '\x9e' - 159: 106, # '\x9f' - 160: 150, # '\xa0' - 161: 149, # '¡' - 162: 148, # '¢' - 163: 147, # '£' - 164: 146, # '¤' - 165: 145, # '¥' - 166: 144, # '¦' - 167: 100, # '§' - 168: 143, # '¨' - 169: 142, # '©' - 170: 141, # 'ª' - 171: 140, # '«' - 172: 139, # '¬' - 173: 138, # '\xad' - 174: 137, # '®' - 175: 136, # '¯' - 176: 94, # '°' - 177: 80, # '±' - 178: 93, # '²' - 179: 135, # '³' - 180: 105, # '´' - 181: 134, # 'µ' - 182: 133, # '¶' - 183: 63, # '·' - 184: 132, # '¸' - 185: 131, # '¹' - 186: 130, # 'º' - 187: 129, # '»' - 188: 128, # '¼' - 189: 127, # '½' - 190: 126, # '¾' - 191: 125, # '¿' - 192: 124, # 'À' - 193: 104, # 'Á' - 194: 73, # 'Â' - 195: 99, # 'Ã' - 196: 79, # 'Ä' - 197: 85, # 'Å' - 198: 123, # 'Æ' - 199: 54, # 'Ç' - 200: 122, # 'È' - 201: 98, # 'É' - 202: 92, # 'Ê' - 203: 121, # 'Ë' - 204: 120, # 'Ì' - 205: 91, # 'Í' - 206: 103, # 'Î' - 207: 119, # 'Ï' - 208: 68, # 'Ğ' - 209: 118, # 'Ñ' - 210: 117, # 'Ò' - 211: 97, # 'Ó' - 212: 116, # 'Ô' - 213: 115, # 'Õ' - 214: 50, # 'Ö' - 215: 90, # '×' - 216: 114, # 'Ø' - 217: 113, # 'Ù' - 218: 112, # 'Ú' - 219: 111, # 'Û' - 220: 55, # 'Ü' - 221: 41, # 'İ' - 222: 40, # 'Ş' - 223: 86, # 'ß' - 224: 89, # 'à' - 225: 70, # 'á' - 226: 59, # 'â' - 227: 78, # 'ã' - 228: 71, # 'ä' - 229: 82, # 'å' - 230: 88, # 'æ' - 231: 33, # 'ç' - 232: 77, # 'è' - 233: 66, # 'é' - 234: 84, # 'ê' - 235: 83, # 'ë' - 236: 110, # 'ì' - 237: 75, # 'í' - 238: 61, # 'î' - 239: 96, # 'ï' - 240: 30, # 'ğ' - 241: 67, # 'ñ' - 242: 109, # 'ò' - 243: 74, # 'ó' - 244: 87, # 'ô' - 245: 102, # 'õ' - 246: 34, # 'ö' - 247: 95, # '÷' - 248: 81, # 'ø' - 249: 108, # 'ù' - 250: 76, # 'ú' - 251: 72, # 'û' - 252: 17, # 'ü' - 253: 6, # 'ı' - 254: 19, # 'ş' - 255: 107, # 'ÿ' -} - -ISO_8859_9_TURKISH_MODEL = SingleByteCharSetModel( - charset_name="ISO-8859-9", - language="Turkish", - char_to_order_map=ISO_8859_9_TURKISH_CHAR_TO_ORDER, - language_model=TURKISH_LANG_MODEL, - typical_positive_ratio=0.97029, - keep_ascii_letters=True, - alphabet="ABCDEFGHIJKLMNOPRSTUVYZabcdefghijklmnoprstuvyzÂÇÎÖÛÜâçîöûüĞğİıŞş", -) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/latin1prober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/latin1prober.py deleted file mode 100644 index 59a01d9..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/latin1prober.py +++ /dev/null @@ -1,147 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import List, Union - -from .charsetprober import CharSetProber -from .enums import ProbingState - -FREQ_CAT_NUM = 4 - -UDF = 0 # undefined -OTH = 1 # other -ASC = 2 # ascii capital letter -ASS = 3 # ascii small letter -ACV = 4 # accent capital vowel -ACO = 5 # accent capital other -ASV = 6 # accent small vowel -ASO = 7 # accent small other -CLASS_NUM = 8 # total classes - -# fmt: off -Latin1_CharToClass = ( - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F - OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 - ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F - ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 - ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F - OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 - ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F - ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 - ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F - OTH, UDF, OTH, ASO, OTH, OTH, OTH, OTH, # 80 - 87 - OTH, OTH, ACO, OTH, ACO, UDF, ACO, UDF, # 88 - 8F - UDF, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 90 - 97 - OTH, OTH, ASO, OTH, ASO, UDF, ASO, ACO, # 98 - 9F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A0 - A7 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # A8 - AF - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B8 - BF - ACV, ACV, ACV, ACV, ACV, ACV, ACO, ACO, # C0 - C7 - ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # C8 - CF - ACO, ACO, ACV, ACV, ACV, ACV, ACV, OTH, # D0 - D7 - ACV, ACV, ACV, ACV, ACV, ACO, ACO, ACO, # D8 - DF - ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASO, # E0 - E7 - ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # E8 - EF - ASO, ASO, ASV, ASV, ASV, ASV, ASV, OTH, # F0 - F7 - ASV, ASV, ASV, ASV, ASV, ASO, ASO, ASO, # F8 - FF -) - -# 0 : illegal -# 1 : very unlikely -# 2 : normal -# 3 : very likely -Latin1ClassModel = ( -# UDF OTH ASC ASS ACV ACO ASV ASO - 0, 0, 0, 0, 0, 0, 0, 0, # UDF - 0, 3, 3, 3, 3, 3, 3, 3, # OTH - 0, 3, 3, 3, 3, 3, 3, 3, # ASC - 0, 3, 3, 3, 1, 1, 3, 3, # ASS - 0, 3, 3, 3, 1, 2, 1, 2, # ACV - 0, 3, 3, 3, 3, 3, 3, 3, # ACO - 0, 3, 1, 3, 1, 1, 1, 3, # ASV - 0, 3, 1, 3, 1, 1, 3, 3, # ASO -) -# fmt: on - - -class Latin1Prober(CharSetProber): - def __init__(self) -> None: - super().__init__() - self._last_char_class = OTH - self._freq_counter: List[int] = [] - self.reset() - - def reset(self) -> None: - self._last_char_class = OTH - self._freq_counter = [0] * FREQ_CAT_NUM - super().reset() - - @property - def charset_name(self) -> str: - return "ISO-8859-1" - - @property - def language(self) -> str: - return "" - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - byte_str = self.remove_xml_tags(byte_str) - for c in byte_str: - char_class = Latin1_CharToClass[c] - freq = Latin1ClassModel[(self._last_char_class * CLASS_NUM) + char_class] - if freq == 0: - self._state = ProbingState.NOT_ME - break - self._freq_counter[freq] += 1 - self._last_char_class = char_class - - return self.state - - def get_confidence(self) -> float: - if self.state == ProbingState.NOT_ME: - return 0.01 - - total = sum(self._freq_counter) - confidence = ( - 0.0 - if total < 0.01 - else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total - ) - confidence = max(confidence, 0.0) - # lower the confidence of latin1 so that other more accurate - # detector can take priority. - confidence *= 0.73 - return confidence diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/macromanprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/macromanprober.py deleted file mode 100644 index 1425d10..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/macromanprober.py +++ /dev/null @@ -1,162 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# This code was modified from latin1prober.py by Rob Speer . -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Rob Speer - adapt to MacRoman encoding -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import List, Union - -from .charsetprober import CharSetProber -from .enums import ProbingState - -FREQ_CAT_NUM = 4 - -UDF = 0 # undefined -OTH = 1 # other -ASC = 2 # ascii capital letter -ASS = 3 # ascii small letter -ACV = 4 # accent capital vowel -ACO = 5 # accent capital other -ASV = 6 # accent small vowel -ASO = 7 # accent small other -ODD = 8 # character that is unlikely to appear -CLASS_NUM = 9 # total classes - -# The change from Latin1 is that we explicitly look for extended characters -# that are infrequently-occurring symbols, and consider them to always be -# improbable. This should let MacRoman get out of the way of more likely -# encodings in most situations. - -# fmt: off -MacRoman_CharToClass = ( - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 00 - 07 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 08 - 0F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 10 - 17 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 18 - 1F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 20 - 27 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 28 - 2F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 30 - 37 - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # 38 - 3F - OTH, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 40 - 47 - ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 48 - 4F - ASC, ASC, ASC, ASC, ASC, ASC, ASC, ASC, # 50 - 57 - ASC, ASC, ASC, OTH, OTH, OTH, OTH, OTH, # 58 - 5F - OTH, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 60 - 67 - ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 68 - 6F - ASS, ASS, ASS, ASS, ASS, ASS, ASS, ASS, # 70 - 77 - ASS, ASS, ASS, OTH, OTH, OTH, OTH, OTH, # 78 - 7F - ACV, ACV, ACO, ACV, ACO, ACV, ACV, ASV, # 80 - 87 - ASV, ASV, ASV, ASV, ASV, ASO, ASV, ASV, # 88 - 8F - ASV, ASV, ASV, ASV, ASV, ASV, ASO, ASV, # 90 - 97 - ASV, ASV, ASV, ASV, ASV, ASV, ASV, ASV, # 98 - 9F - OTH, OTH, OTH, OTH, OTH, OTH, OTH, ASO, # A0 - A7 - OTH, OTH, ODD, ODD, OTH, OTH, ACV, ACV, # A8 - AF - OTH, OTH, OTH, OTH, OTH, OTH, OTH, OTH, # B0 - B7 - OTH, OTH, OTH, OTH, OTH, OTH, ASV, ASV, # B8 - BF - OTH, OTH, ODD, OTH, ODD, OTH, OTH, OTH, # C0 - C7 - OTH, OTH, OTH, ACV, ACV, ACV, ACV, ASV, # C8 - CF - OTH, OTH, OTH, OTH, OTH, OTH, OTH, ODD, # D0 - D7 - ASV, ACV, ODD, OTH, OTH, OTH, OTH, OTH, # D8 - DF - OTH, OTH, OTH, OTH, OTH, ACV, ACV, ACV, # E0 - E7 - ACV, ACV, ACV, ACV, ACV, ACV, ACV, ACV, # E8 - EF - ODD, ACV, ACV, ACV, ACV, ASV, ODD, ODD, # F0 - F7 - ODD, ODD, ODD, ODD, ODD, ODD, ODD, ODD, # F8 - FF -) - -# 0 : illegal -# 1 : very unlikely -# 2 : normal -# 3 : very likely -MacRomanClassModel = ( -# UDF OTH ASC ASS ACV ACO ASV ASO ODD - 0, 0, 0, 0, 0, 0, 0, 0, 0, # UDF - 0, 3, 3, 3, 3, 3, 3, 3, 1, # OTH - 0, 3, 3, 3, 3, 3, 3, 3, 1, # ASC - 0, 3, 3, 3, 1, 1, 3, 3, 1, # ASS - 0, 3, 3, 3, 1, 2, 1, 2, 1, # ACV - 0, 3, 3, 3, 3, 3, 3, 3, 1, # ACO - 0, 3, 1, 3, 1, 1, 1, 3, 1, # ASV - 0, 3, 1, 3, 1, 1, 3, 3, 1, # ASO - 0, 1, 1, 1, 1, 1, 1, 1, 1, # ODD -) -# fmt: on - - -class MacRomanProber(CharSetProber): - def __init__(self) -> None: - super().__init__() - self._last_char_class = OTH - self._freq_counter: List[int] = [] - self.reset() - - def reset(self) -> None: - self._last_char_class = OTH - self._freq_counter = [0] * FREQ_CAT_NUM - - # express the prior that MacRoman is a somewhat rare encoding; - # this can be done by starting out in a slightly improbable state - # that must be overcome - self._freq_counter[2] = 10 - - super().reset() - - @property - def charset_name(self) -> str: - return "MacRoman" - - @property - def language(self) -> str: - return "" - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - byte_str = self.remove_xml_tags(byte_str) - for c in byte_str: - char_class = MacRoman_CharToClass[c] - freq = MacRomanClassModel[(self._last_char_class * CLASS_NUM) + char_class] - if freq == 0: - self._state = ProbingState.NOT_ME - break - self._freq_counter[freq] += 1 - self._last_char_class = char_class - - return self.state - - def get_confidence(self) -> float: - if self.state == ProbingState.NOT_ME: - return 0.01 - - total = sum(self._freq_counter) - confidence = ( - 0.0 - if total < 0.01 - else (self._freq_counter[3] - self._freq_counter[1] * 20.0) / total - ) - confidence = max(confidence, 0.0) - # lower the confidence of MacRoman so that other more accurate - # detector can take priority. - confidence *= 0.73 - return confidence diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcharsetprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcharsetprober.py deleted file mode 100644 index 666307e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcharsetprober.py +++ /dev/null @@ -1,95 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# Proofpoint, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Optional, Union - -from .chardistribution import CharDistributionAnalysis -from .charsetprober import CharSetProber -from .codingstatemachine import CodingStateMachine -from .enums import LanguageFilter, MachineState, ProbingState - - -class MultiByteCharSetProber(CharSetProber): - """ - MultiByteCharSetProber - """ - - def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: - super().__init__(lang_filter=lang_filter) - self.distribution_analyzer: Optional[CharDistributionAnalysis] = None - self.coding_sm: Optional[CodingStateMachine] = None - self._last_char = bytearray(b"\0\0") - - def reset(self) -> None: - super().reset() - if self.coding_sm: - self.coding_sm.reset() - if self.distribution_analyzer: - self.distribution_analyzer.reset() - self._last_char = bytearray(b"\0\0") - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - assert self.coding_sm is not None - assert self.distribution_analyzer is not None - - for i, byte in enumerate(byte_str): - coding_state = self.coding_sm.next_state(byte) - if coding_state == MachineState.ERROR: - self.logger.debug( - "%s %s prober hit error at byte %s", - self.charset_name, - self.language, - i, - ) - self._state = ProbingState.NOT_ME - break - if coding_state == MachineState.ITS_ME: - self._state = ProbingState.FOUND_IT - break - if coding_state == MachineState.START: - char_len = self.coding_sm.get_current_charlen() - if i == 0: - self._last_char[1] = byte - self.distribution_analyzer.feed(self._last_char, char_len) - else: - self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len) - - self._last_char[0] = byte_str[-1] - - if self.state == ProbingState.DETECTING: - if self.distribution_analyzer.got_enough_data() and ( - self.get_confidence() > self.SHORTCUT_THRESHOLD - ): - self._state = ProbingState.FOUND_IT - - return self.state - - def get_confidence(self) -> float: - assert self.distribution_analyzer is not None - return self.distribution_analyzer.get_confidence() diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcsgroupprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcsgroupprober.py deleted file mode 100644 index 6cb9cc7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcsgroupprober.py +++ /dev/null @@ -1,57 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# Proofpoint, Inc. -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .big5prober import Big5Prober -from .charsetgroupprober import CharSetGroupProber -from .cp949prober import CP949Prober -from .enums import LanguageFilter -from .eucjpprober import EUCJPProber -from .euckrprober import EUCKRProber -from .euctwprober import EUCTWProber -from .gb2312prober import GB2312Prober -from .johabprober import JOHABProber -from .sjisprober import SJISProber -from .utf8prober import UTF8Prober - - -class MBCSGroupProber(CharSetGroupProber): - def __init__(self, lang_filter: LanguageFilter = LanguageFilter.NONE) -> None: - super().__init__(lang_filter=lang_filter) - self.probers = [ - UTF8Prober(), - SJISProber(), - EUCJPProber(), - GB2312Prober(), - EUCKRProber(), - CP949Prober(), - Big5Prober(), - EUCTWProber(), - JOHABProber(), - ] - self.reset() diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcssm.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcssm.py deleted file mode 100644 index 7bbe97e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/mbcssm.py +++ /dev/null @@ -1,661 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .codingstatemachinedict import CodingStateMachineDict -from .enums import MachineState - -# BIG5 - -# fmt: off -BIG5_CLS = ( - 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as legal value - 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f - 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 - 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f - 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 - 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f - 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 - 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f - 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 - 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f - 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 - 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f - 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 - 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f - 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 - 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f - 4, 4, 4, 4, 4, 4, 4, 4, # 80 - 87 - 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f - 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97 - 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f - 4, 3, 3, 3, 3, 3, 3, 3, # a0 - a7 - 3, 3, 3, 3, 3, 3, 3, 3, # a8 - af - 3, 3, 3, 3, 3, 3, 3, 3, # b0 - b7 - 3, 3, 3, 3, 3, 3, 3, 3, # b8 - bf - 3, 3, 3, 3, 3, 3, 3, 3, # c0 - c7 - 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf - 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7 - 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df - 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 - 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef - 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 - 3, 3, 3, 3, 3, 3, 3, 0 # f8 - ff -) - -BIG5_ST = ( - MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,#08-0f - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START#10-17 -) -# fmt: on - -BIG5_CHAR_LEN_TABLE = (0, 1, 1, 2, 0) - -BIG5_SM_MODEL: CodingStateMachineDict = { - "class_table": BIG5_CLS, - "class_factor": 5, - "state_table": BIG5_ST, - "char_len_table": BIG5_CHAR_LEN_TABLE, - "name": "Big5", -} - -# CP949 -# fmt: off -CP949_CLS = ( - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, # 00 - 0f - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, # 10 - 1f - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 2f - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 3f - 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 4f - 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 50 - 5f - 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, # 60 - 6f - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 1, 1, # 70 - 7f - 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 80 - 8f - 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 9f - 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, # a0 - af - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, # b0 - bf - 7, 7, 7, 7, 7, 7, 9, 2, 2, 3, 2, 2, 2, 2, 2, 2, # c0 - cf - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # d0 - df - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, # e0 - ef - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, # f0 - ff -) - -CP949_ST = ( -#cls= 0 1 2 3 4 5 6 7 8 9 # previous state = - MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.START,MachineState.START, 4, 5,MachineState.ERROR, 6, # MachineState.START - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, # MachineState.ERROR - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 3 - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 4 - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, # 5 - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START, # 6 -) -# fmt: on - -CP949_CHAR_LEN_TABLE = (0, 1, 2, 0, 1, 1, 2, 2, 0, 2) - -CP949_SM_MODEL: CodingStateMachineDict = { - "class_table": CP949_CLS, - "class_factor": 10, - "state_table": CP949_ST, - "char_len_table": CP949_CHAR_LEN_TABLE, - "name": "CP949", -} - -# EUC-JP -# fmt: off -EUCJP_CLS = ( - 4, 4, 4, 4, 4, 4, 4, 4, # 00 - 07 - 4, 4, 4, 4, 4, 4, 5, 5, # 08 - 0f - 4, 4, 4, 4, 4, 4, 4, 4, # 10 - 17 - 4, 4, 4, 5, 4, 4, 4, 4, # 18 - 1f - 4, 4, 4, 4, 4, 4, 4, 4, # 20 - 27 - 4, 4, 4, 4, 4, 4, 4, 4, # 28 - 2f - 4, 4, 4, 4, 4, 4, 4, 4, # 30 - 37 - 4, 4, 4, 4, 4, 4, 4, 4, # 38 - 3f - 4, 4, 4, 4, 4, 4, 4, 4, # 40 - 47 - 4, 4, 4, 4, 4, 4, 4, 4, # 48 - 4f - 4, 4, 4, 4, 4, 4, 4, 4, # 50 - 57 - 4, 4, 4, 4, 4, 4, 4, 4, # 58 - 5f - 4, 4, 4, 4, 4, 4, 4, 4, # 60 - 67 - 4, 4, 4, 4, 4, 4, 4, 4, # 68 - 6f - 4, 4, 4, 4, 4, 4, 4, 4, # 70 - 77 - 4, 4, 4, 4, 4, 4, 4, 4, # 78 - 7f - 5, 5, 5, 5, 5, 5, 5, 5, # 80 - 87 - 5, 5, 5, 5, 5, 5, 1, 3, # 88 - 8f - 5, 5, 5, 5, 5, 5, 5, 5, # 90 - 97 - 5, 5, 5, 5, 5, 5, 5, 5, # 98 - 9f - 5, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 - 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af - 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 - 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf - 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 - 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf - 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 - 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df - 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 - 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef - 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 - 0, 0, 0, 0, 0, 0, 0, 5 # f8 - ff -) - -EUCJP_ST = ( - 3, 4, 3, 5,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 3,MachineState.ERROR,#18-1f - 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START#20-27 -) -# fmt: on - -EUCJP_CHAR_LEN_TABLE = (2, 2, 2, 3, 1, 0) - -EUCJP_SM_MODEL: CodingStateMachineDict = { - "class_table": EUCJP_CLS, - "class_factor": 6, - "state_table": EUCJP_ST, - "char_len_table": EUCJP_CHAR_LEN_TABLE, - "name": "EUC-JP", -} - -# EUC-KR -# fmt: off -EUCKR_CLS = ( - 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 - 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f - 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 - 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f - 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 - 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f - 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 - 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f - 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47 - 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f - 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57 - 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f - 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67 - 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f - 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77 - 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f - 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 - 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f - 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 - 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f - 0, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 - 2, 2, 2, 2, 2, 3, 3, 3, # a8 - af - 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 - 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf - 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 - 2, 3, 2, 2, 2, 2, 2, 2, # c8 - cf - 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 - 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df - 2, 2, 2, 2, 2, 2, 2, 2, # e0 - e7 - 2, 2, 2, 2, 2, 2, 2, 2, # e8 - ef - 2, 2, 2, 2, 2, 2, 2, 2, # f0 - f7 - 2, 2, 2, 2, 2, 2, 2, 0 # f8 - ff -) - -EUCKR_ST = ( - MachineState.ERROR,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #08-0f -) -# fmt: on - -EUCKR_CHAR_LEN_TABLE = (0, 1, 2, 0) - -EUCKR_SM_MODEL: CodingStateMachineDict = { - "class_table": EUCKR_CLS, - "class_factor": 4, - "state_table": EUCKR_ST, - "char_len_table": EUCKR_CHAR_LEN_TABLE, - "name": "EUC-KR", -} - -# JOHAB -# fmt: off -JOHAB_CLS = ( - 4,4,4,4,4,4,4,4, # 00 - 07 - 4,4,4,4,4,4,0,0, # 08 - 0f - 4,4,4,4,4,4,4,4, # 10 - 17 - 4,4,4,0,4,4,4,4, # 18 - 1f - 4,4,4,4,4,4,4,4, # 20 - 27 - 4,4,4,4,4,4,4,4, # 28 - 2f - 4,3,3,3,3,3,3,3, # 30 - 37 - 3,3,3,3,3,3,3,3, # 38 - 3f - 3,1,1,1,1,1,1,1, # 40 - 47 - 1,1,1,1,1,1,1,1, # 48 - 4f - 1,1,1,1,1,1,1,1, # 50 - 57 - 1,1,1,1,1,1,1,1, # 58 - 5f - 1,1,1,1,1,1,1,1, # 60 - 67 - 1,1,1,1,1,1,1,1, # 68 - 6f - 1,1,1,1,1,1,1,1, # 70 - 77 - 1,1,1,1,1,1,1,2, # 78 - 7f - 6,6,6,6,8,8,8,8, # 80 - 87 - 8,8,8,8,8,8,8,8, # 88 - 8f - 8,7,7,7,7,7,7,7, # 90 - 97 - 7,7,7,7,7,7,7,7, # 98 - 9f - 7,7,7,7,7,7,7,7, # a0 - a7 - 7,7,7,7,7,7,7,7, # a8 - af - 7,7,7,7,7,7,7,7, # b0 - b7 - 7,7,7,7,7,7,7,7, # b8 - bf - 7,7,7,7,7,7,7,7, # c0 - c7 - 7,7,7,7,7,7,7,7, # c8 - cf - 7,7,7,7,5,5,5,5, # d0 - d7 - 5,9,9,9,9,9,9,5, # d8 - df - 9,9,9,9,9,9,9,9, # e0 - e7 - 9,9,9,9,9,9,9,9, # e8 - ef - 9,9,9,9,9,9,9,9, # f0 - f7 - 9,9,5,5,5,5,5,0 # f8 - ff -) - -JOHAB_ST = ( -# cls = 0 1 2 3 4 5 6 7 8 9 - MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.ERROR ,MachineState.ERROR ,3 ,3 ,4 , # MachineState.START - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME, # MachineState.ITS_ME - MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR ,MachineState.ERROR , # MachineState.ERROR - MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.ERROR ,MachineState.ERROR ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START ,MachineState.START , # 3 - MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START ,MachineState.ERROR ,MachineState.START , # 4 -) -# fmt: on - -JOHAB_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 0, 0, 2, 2, 2) - -JOHAB_SM_MODEL: CodingStateMachineDict = { - "class_table": JOHAB_CLS, - "class_factor": 10, - "state_table": JOHAB_ST, - "char_len_table": JOHAB_CHAR_LEN_TABLE, - "name": "Johab", -} - -# EUC-TW -# fmt: off -EUCTW_CLS = ( - 2, 2, 2, 2, 2, 2, 2, 2, # 00 - 07 - 2, 2, 2, 2, 2, 2, 0, 0, # 08 - 0f - 2, 2, 2, 2, 2, 2, 2, 2, # 10 - 17 - 2, 2, 2, 0, 2, 2, 2, 2, # 18 - 1f - 2, 2, 2, 2, 2, 2, 2, 2, # 20 - 27 - 2, 2, 2, 2, 2, 2, 2, 2, # 28 - 2f - 2, 2, 2, 2, 2, 2, 2, 2, # 30 - 37 - 2, 2, 2, 2, 2, 2, 2, 2, # 38 - 3f - 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 - 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f - 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 - 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f - 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 - 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f - 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 - 2, 2, 2, 2, 2, 2, 2, 2, # 78 - 7f - 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 - 0, 0, 0, 0, 0, 0, 6, 0, # 88 - 8f - 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 - 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f - 0, 3, 4, 4, 4, 4, 4, 4, # a0 - a7 - 5, 5, 1, 1, 1, 1, 1, 1, # a8 - af - 1, 1, 1, 1, 1, 1, 1, 1, # b0 - b7 - 1, 1, 1, 1, 1, 1, 1, 1, # b8 - bf - 1, 1, 3, 1, 3, 3, 3, 3, # c0 - c7 - 3, 3, 3, 3, 3, 3, 3, 3, # c8 - cf - 3, 3, 3, 3, 3, 3, 3, 3, # d0 - d7 - 3, 3, 3, 3, 3, 3, 3, 3, # d8 - df - 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 - 3, 3, 3, 3, 3, 3, 3, 3, # e8 - ef - 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 - 3, 3, 3, 3, 3, 3, 3, 0 # f8 - ff -) - -EUCTW_ST = ( - MachineState.ERROR,MachineState.ERROR,MachineState.START, 3, 3, 3, 4,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.START,MachineState.ERROR,#10-17 - MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f - 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,#20-27 - MachineState.START,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f -) -# fmt: on - -EUCTW_CHAR_LEN_TABLE = (0, 0, 1, 2, 2, 2, 3) - -EUCTW_SM_MODEL: CodingStateMachineDict = { - "class_table": EUCTW_CLS, - "class_factor": 7, - "state_table": EUCTW_ST, - "char_len_table": EUCTW_CHAR_LEN_TABLE, - "name": "x-euc-tw", -} - -# GB2312 -# fmt: off -GB2312_CLS = ( - 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 - 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f - 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 - 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f - 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 - 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f - 3, 3, 3, 3, 3, 3, 3, 3, # 30 - 37 - 3, 3, 1, 1, 1, 1, 1, 1, # 38 - 3f - 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 - 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f - 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 - 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f - 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 - 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f - 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 - 2, 2, 2, 2, 2, 2, 2, 4, # 78 - 7f - 5, 6, 6, 6, 6, 6, 6, 6, # 80 - 87 - 6, 6, 6, 6, 6, 6, 6, 6, # 88 - 8f - 6, 6, 6, 6, 6, 6, 6, 6, # 90 - 97 - 6, 6, 6, 6, 6, 6, 6, 6, # 98 - 9f - 6, 6, 6, 6, 6, 6, 6, 6, # a0 - a7 - 6, 6, 6, 6, 6, 6, 6, 6, # a8 - af - 6, 6, 6, 6, 6, 6, 6, 6, # b0 - b7 - 6, 6, 6, 6, 6, 6, 6, 6, # b8 - bf - 6, 6, 6, 6, 6, 6, 6, 6, # c0 - c7 - 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf - 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7 - 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df - 6, 6, 6, 6, 6, 6, 6, 6, # e0 - e7 - 6, 6, 6, 6, 6, 6, 6, 6, # e8 - ef - 6, 6, 6, 6, 6, 6, 6, 6, # f0 - f7 - 6, 6, 6, 6, 6, 6, 6, 0 # f8 - ff -) - -GB2312_ST = ( - MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START, 3,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,#10-17 - 4,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f - MachineState.ERROR,MachineState.ERROR, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#20-27 - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.START #28-2f -) -# fmt: on - -# To be accurate, the length of class 6 can be either 2 or 4. -# But it is not necessary to discriminate between the two since -# it is used for frequency analysis only, and we are validating -# each code range there as well. So it is safe to set it to be -# 2 here. -GB2312_CHAR_LEN_TABLE = (0, 1, 1, 1, 1, 1, 2) - -GB2312_SM_MODEL: CodingStateMachineDict = { - "class_table": GB2312_CLS, - "class_factor": 7, - "state_table": GB2312_ST, - "char_len_table": GB2312_CHAR_LEN_TABLE, - "name": "GB2312", -} - -# Shift_JIS -# fmt: off -SJIS_CLS = ( - 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 - 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f - 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 - 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f - 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 - 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f - 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 - 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f - 2, 2, 2, 2, 2, 2, 2, 2, # 40 - 47 - 2, 2, 2, 2, 2, 2, 2, 2, # 48 - 4f - 2, 2, 2, 2, 2, 2, 2, 2, # 50 - 57 - 2, 2, 2, 2, 2, 2, 2, 2, # 58 - 5f - 2, 2, 2, 2, 2, 2, 2, 2, # 60 - 67 - 2, 2, 2, 2, 2, 2, 2, 2, # 68 - 6f - 2, 2, 2, 2, 2, 2, 2, 2, # 70 - 77 - 2, 2, 2, 2, 2, 2, 2, 1, # 78 - 7f - 3, 3, 3, 3, 3, 2, 2, 3, # 80 - 87 - 3, 3, 3, 3, 3, 3, 3, 3, # 88 - 8f - 3, 3, 3, 3, 3, 3, 3, 3, # 90 - 97 - 3, 3, 3, 3, 3, 3, 3, 3, # 98 - 9f - #0xa0 is illegal in sjis encoding, but some pages does - #contain such byte. We need to be more error forgiven. - 2, 2, 2, 2, 2, 2, 2, 2, # a0 - a7 - 2, 2, 2, 2, 2, 2, 2, 2, # a8 - af - 2, 2, 2, 2, 2, 2, 2, 2, # b0 - b7 - 2, 2, 2, 2, 2, 2, 2, 2, # b8 - bf - 2, 2, 2, 2, 2, 2, 2, 2, # c0 - c7 - 2, 2, 2, 2, 2, 2, 2, 2, # c8 - cf - 2, 2, 2, 2, 2, 2, 2, 2, # d0 - d7 - 2, 2, 2, 2, 2, 2, 2, 2, # d8 - df - 3, 3, 3, 3, 3, 3, 3, 3, # e0 - e7 - 3, 3, 3, 3, 3, 4, 4, 4, # e8 - ef - 3, 3, 3, 3, 3, 3, 3, 3, # f0 - f7 - 3, 3, 3, 3, 3, 0, 0, 0, # f8 - ff -) - -SJIS_ST = ( - MachineState.ERROR,MachineState.START,MachineState.START, 3,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START #10-17 -) -# fmt: on - -SJIS_CHAR_LEN_TABLE = (0, 1, 1, 2, 0, 0) - -SJIS_SM_MODEL: CodingStateMachineDict = { - "class_table": SJIS_CLS, - "class_factor": 6, - "state_table": SJIS_ST, - "char_len_table": SJIS_CHAR_LEN_TABLE, - "name": "Shift_JIS", -} - -# UCS2-BE -# fmt: off -UCS2BE_CLS = ( - 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 - 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f - 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 - 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f - 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 - 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f - 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 - 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f - 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 - 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f - 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 - 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f - 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 - 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f - 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 - 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f - 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 - 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f - 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 - 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f - 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7 - 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af - 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7 - 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf - 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7 - 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf - 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7 - 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df - 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 - 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef - 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 - 0, 0, 0, 0, 0, 0, 4, 5 # f8 - ff -) - -UCS2BE_ST = ( - 5, 7, 7,MachineState.ERROR, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME, 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,#10-17 - 6, 6, 6, 6, 6,MachineState.ITS_ME, 6, 6,#18-1f - 6, 6, 6, 6, 5, 7, 7,MachineState.ERROR,#20-27 - 5, 8, 6, 6,MachineState.ERROR, 6, 6, 6,#28-2f - 6, 6, 6, 6,MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START #30-37 -) -# fmt: on - -UCS2BE_CHAR_LEN_TABLE = (2, 2, 2, 0, 2, 2) - -UCS2BE_SM_MODEL: CodingStateMachineDict = { - "class_table": UCS2BE_CLS, - "class_factor": 6, - "state_table": UCS2BE_ST, - "char_len_table": UCS2BE_CHAR_LEN_TABLE, - "name": "UTF-16BE", -} - -# UCS2-LE -# fmt: off -UCS2LE_CLS = ( - 0, 0, 0, 0, 0, 0, 0, 0, # 00 - 07 - 0, 0, 1, 0, 0, 2, 0, 0, # 08 - 0f - 0, 0, 0, 0, 0, 0, 0, 0, # 10 - 17 - 0, 0, 0, 3, 0, 0, 0, 0, # 18 - 1f - 0, 0, 0, 0, 0, 0, 0, 0, # 20 - 27 - 0, 3, 3, 3, 3, 3, 0, 0, # 28 - 2f - 0, 0, 0, 0, 0, 0, 0, 0, # 30 - 37 - 0, 0, 0, 0, 0, 0, 0, 0, # 38 - 3f - 0, 0, 0, 0, 0, 0, 0, 0, # 40 - 47 - 0, 0, 0, 0, 0, 0, 0, 0, # 48 - 4f - 0, 0, 0, 0, 0, 0, 0, 0, # 50 - 57 - 0, 0, 0, 0, 0, 0, 0, 0, # 58 - 5f - 0, 0, 0, 0, 0, 0, 0, 0, # 60 - 67 - 0, 0, 0, 0, 0, 0, 0, 0, # 68 - 6f - 0, 0, 0, 0, 0, 0, 0, 0, # 70 - 77 - 0, 0, 0, 0, 0, 0, 0, 0, # 78 - 7f - 0, 0, 0, 0, 0, 0, 0, 0, # 80 - 87 - 0, 0, 0, 0, 0, 0, 0, 0, # 88 - 8f - 0, 0, 0, 0, 0, 0, 0, 0, # 90 - 97 - 0, 0, 0, 0, 0, 0, 0, 0, # 98 - 9f - 0, 0, 0, 0, 0, 0, 0, 0, # a0 - a7 - 0, 0, 0, 0, 0, 0, 0, 0, # a8 - af - 0, 0, 0, 0, 0, 0, 0, 0, # b0 - b7 - 0, 0, 0, 0, 0, 0, 0, 0, # b8 - bf - 0, 0, 0, 0, 0, 0, 0, 0, # c0 - c7 - 0, 0, 0, 0, 0, 0, 0, 0, # c8 - cf - 0, 0, 0, 0, 0, 0, 0, 0, # d0 - d7 - 0, 0, 0, 0, 0, 0, 0, 0, # d8 - df - 0, 0, 0, 0, 0, 0, 0, 0, # e0 - e7 - 0, 0, 0, 0, 0, 0, 0, 0, # e8 - ef - 0, 0, 0, 0, 0, 0, 0, 0, # f0 - f7 - 0, 0, 0, 0, 0, 0, 4, 5 # f8 - ff -) - -UCS2LE_ST = ( - 6, 6, 7, 6, 4, 3,MachineState.ERROR,MachineState.ERROR,#00-07 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#08-0f - MachineState.ITS_ME,MachineState.ITS_ME, 5, 5, 5,MachineState.ERROR,MachineState.ITS_ME,MachineState.ERROR,#10-17 - 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR, 6, 6,#18-1f - 7, 6, 8, 8, 5, 5, 5,MachineState.ERROR,#20-27 - 5, 5, 5,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5,#28-2f - 5, 5, 5,MachineState.ERROR, 5,MachineState.ERROR,MachineState.START,MachineState.START #30-37 -) -# fmt: on - -UCS2LE_CHAR_LEN_TABLE = (2, 2, 2, 2, 2, 2) - -UCS2LE_SM_MODEL: CodingStateMachineDict = { - "class_table": UCS2LE_CLS, - "class_factor": 6, - "state_table": UCS2LE_ST, - "char_len_table": UCS2LE_CHAR_LEN_TABLE, - "name": "UTF-16LE", -} - -# UTF-8 -# fmt: off -UTF8_CLS = ( - 1, 1, 1, 1, 1, 1, 1, 1, # 00 - 07 #allow 0x00 as a legal value - 1, 1, 1, 1, 1, 1, 0, 0, # 08 - 0f - 1, 1, 1, 1, 1, 1, 1, 1, # 10 - 17 - 1, 1, 1, 0, 1, 1, 1, 1, # 18 - 1f - 1, 1, 1, 1, 1, 1, 1, 1, # 20 - 27 - 1, 1, 1, 1, 1, 1, 1, 1, # 28 - 2f - 1, 1, 1, 1, 1, 1, 1, 1, # 30 - 37 - 1, 1, 1, 1, 1, 1, 1, 1, # 38 - 3f - 1, 1, 1, 1, 1, 1, 1, 1, # 40 - 47 - 1, 1, 1, 1, 1, 1, 1, 1, # 48 - 4f - 1, 1, 1, 1, 1, 1, 1, 1, # 50 - 57 - 1, 1, 1, 1, 1, 1, 1, 1, # 58 - 5f - 1, 1, 1, 1, 1, 1, 1, 1, # 60 - 67 - 1, 1, 1, 1, 1, 1, 1, 1, # 68 - 6f - 1, 1, 1, 1, 1, 1, 1, 1, # 70 - 77 - 1, 1, 1, 1, 1, 1, 1, 1, # 78 - 7f - 2, 2, 2, 2, 3, 3, 3, 3, # 80 - 87 - 4, 4, 4, 4, 4, 4, 4, 4, # 88 - 8f - 4, 4, 4, 4, 4, 4, 4, 4, # 90 - 97 - 4, 4, 4, 4, 4, 4, 4, 4, # 98 - 9f - 5, 5, 5, 5, 5, 5, 5, 5, # a0 - a7 - 5, 5, 5, 5, 5, 5, 5, 5, # a8 - af - 5, 5, 5, 5, 5, 5, 5, 5, # b0 - b7 - 5, 5, 5, 5, 5, 5, 5, 5, # b8 - bf - 0, 0, 6, 6, 6, 6, 6, 6, # c0 - c7 - 6, 6, 6, 6, 6, 6, 6, 6, # c8 - cf - 6, 6, 6, 6, 6, 6, 6, 6, # d0 - d7 - 6, 6, 6, 6, 6, 6, 6, 6, # d8 - df - 7, 8, 8, 8, 8, 8, 8, 8, # e0 - e7 - 8, 8, 8, 8, 8, 9, 8, 8, # e8 - ef - 10, 11, 11, 11, 11, 11, 11, 11, # f0 - f7 - 12, 13, 13, 13, 14, 15, 0, 0 # f8 - ff -) - -UTF8_ST = ( - MachineState.ERROR,MachineState.START,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12, 10,#00-07 - 9, 11, 8, 7, 6, 5, 4, 3,#08-0f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#10-17 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#18-1f - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#20-27 - MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,MachineState.ITS_ME,#28-2f - MachineState.ERROR,MachineState.ERROR, 5, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#30-37 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#38-3f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 5, 5, 5,MachineState.ERROR,MachineState.ERROR,#40-47 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#48-4f - MachineState.ERROR,MachineState.ERROR, 7, 7, 7, 7,MachineState.ERROR,MachineState.ERROR,#50-57 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#58-5f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 7, 7,MachineState.ERROR,MachineState.ERROR,#60-67 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#68-6f - MachineState.ERROR,MachineState.ERROR, 9, 9, 9, 9,MachineState.ERROR,MachineState.ERROR,#70-77 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#78-7f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 9,MachineState.ERROR,MachineState.ERROR,#80-87 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#88-8f - MachineState.ERROR,MachineState.ERROR, 12, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,#90-97 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#98-9f - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR, 12,MachineState.ERROR,MachineState.ERROR,#a0-a7 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#a8-af - MachineState.ERROR,MachineState.ERROR, 12, 12, 12,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b0-b7 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,#b8-bf - MachineState.ERROR,MachineState.ERROR,MachineState.START,MachineState.START,MachineState.START,MachineState.START,MachineState.ERROR,MachineState.ERROR,#c0-c7 - MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR,MachineState.ERROR #c8-cf -) -# fmt: on - -UTF8_CHAR_LEN_TABLE = (0, 1, 0, 0, 0, 0, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6) - -UTF8_SM_MODEL: CodingStateMachineDict = { - "class_table": UTF8_CLS, - "class_factor": 16, - "state_table": UTF8_ST, - "char_len_table": UTF8_CHAR_LEN_TABLE, - "name": "UTF-8", -} diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/metadata/__init__.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/metadata/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/metadata/languages.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/metadata/languages.py deleted file mode 100644 index eb40c5f..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/metadata/languages.py +++ /dev/null @@ -1,352 +0,0 @@ -""" -Metadata about languages used by our model training code for our -SingleByteCharSetProbers. Could be used for other things in the future. - -This code is based on the language metadata from the uchardet project. -""" - -from string import ascii_letters -from typing import List, Optional - -# TODO: Add Ukrainian (KOI8-U) - - -class Language: - """Metadata about a language useful for training models - - :ivar name: The human name for the language, in English. - :type name: str - :ivar iso_code: 2-letter ISO 639-1 if possible, 3-letter ISO code otherwise, - or use another catalog as a last resort. - :type iso_code: str - :ivar use_ascii: Whether or not ASCII letters should be included in trained - models. - :type use_ascii: bool - :ivar charsets: The charsets we want to support and create data for. - :type charsets: list of str - :ivar alphabet: The characters in the language's alphabet. If `use_ascii` is - `True`, you only need to add those not in the ASCII set. - :type alphabet: str - :ivar wiki_start_pages: The Wikipedia pages to start from if we're crawling - Wikipedia for training data. - :type wiki_start_pages: list of str - """ - - def __init__( - self, - name: Optional[str] = None, - iso_code: Optional[str] = None, - use_ascii: bool = True, - charsets: Optional[List[str]] = None, - alphabet: Optional[str] = None, - wiki_start_pages: Optional[List[str]] = None, - ) -> None: - super().__init__() - self.name = name - self.iso_code = iso_code - self.use_ascii = use_ascii - self.charsets = charsets - if self.use_ascii: - if alphabet: - alphabet += ascii_letters - else: - alphabet = ascii_letters - elif not alphabet: - raise ValueError("Must supply alphabet if use_ascii is False") - self.alphabet = "".join(sorted(set(alphabet))) if alphabet else None - self.wiki_start_pages = wiki_start_pages - - def __repr__(self) -> str: - param_str = ", ".join( - f"{k}={v!r}" for k, v in self.__dict__.items() if not k.startswith("_") - ) - return f"{self.__class__.__name__}({param_str})" - - -LANGUAGES = { - "Arabic": Language( - name="Arabic", - iso_code="ar", - use_ascii=False, - # We only support encodings that use isolated - # forms, because the current recommendation is - # that the rendering system handles presentation - # forms. This means we purposefully skip IBM864. - charsets=["ISO-8859-6", "WINDOWS-1256", "CP720", "CP864"], - alphabet="ءآأؤإئابةتثجحخدذرزسشصضطظعغػؼؽؾؿـفقكلمنهوىيًٌٍَُِّ", - wiki_start_pages=["الصفحة_الرئيسية"], - ), - "Belarusian": Language( - name="Belarusian", - iso_code="be", - use_ascii=False, - charsets=["ISO-8859-5", "WINDOWS-1251", "IBM866", "MacCyrillic"], - alphabet="АБВГДЕЁЖЗІЙКЛМНОПРСТУЎФХЦЧШЫЬЭЮЯабвгдеёжзійклмнопрстуўфхцчшыьэюяʼ", - wiki_start_pages=["Галоўная_старонка"], - ), - "Bulgarian": Language( - name="Bulgarian", - iso_code="bg", - use_ascii=False, - charsets=["ISO-8859-5", "WINDOWS-1251", "IBM855"], - alphabet="АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЬЮЯабвгдежзийклмнопрстуфхцчшщъьюя", - wiki_start_pages=["Начална_страница"], - ), - "Czech": Language( - name="Czech", - iso_code="cz", - use_ascii=True, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="áčďéěíňóřšťúůýžÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ", - wiki_start_pages=["Hlavní_strana"], - ), - "Danish": Language( - name="Danish", - iso_code="da", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="æøåÆØÅ", - wiki_start_pages=["Forside"], - ), - "German": Language( - name="German", - iso_code="de", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="äöüßẞÄÖÜ", - wiki_start_pages=["Wikipedia:Hauptseite"], - ), - "Greek": Language( - name="Greek", - iso_code="el", - use_ascii=False, - charsets=["ISO-8859-7", "WINDOWS-1253"], - alphabet="αβγδεζηθικλμνξοπρσςτυφχψωάέήίόύώΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΣΤΥΦΧΨΩΆΈΉΊΌΎΏ", - wiki_start_pages=["Πύλη:Κύρια"], - ), - "English": Language( - name="English", - iso_code="en", - use_ascii=True, - charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"], - wiki_start_pages=["Main_Page"], - ), - "Esperanto": Language( - name="Esperanto", - iso_code="eo", - # Q, W, X, and Y not used at all - use_ascii=False, - charsets=["ISO-8859-3"], - alphabet="abcĉdefgĝhĥijĵklmnoprsŝtuŭvzABCĈDEFGĜHĤIJĴKLMNOPRSŜTUŬVZ", - wiki_start_pages=["Vikipedio:Ĉefpaĝo"], - ), - "Spanish": Language( - name="Spanish", - iso_code="es", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="ñáéíóúüÑÁÉÍÓÚÜ", - wiki_start_pages=["Wikipedia:Portada"], - ), - "Estonian": Language( - name="Estonian", - iso_code="et", - use_ascii=False, - charsets=["ISO-8859-4", "ISO-8859-13", "WINDOWS-1257"], - # C, F, Š, Q, W, X, Y, Z, Ž are only for - # loanwords - alphabet="ABDEGHIJKLMNOPRSTUVÕÄÖÜabdeghijklmnoprstuvõäöü", - wiki_start_pages=["Esileht"], - ), - "Finnish": Language( - name="Finnish", - iso_code="fi", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="ÅÄÖŠŽåäöšž", - wiki_start_pages=["Wikipedia:Etusivu"], - ), - "French": Language( - name="French", - iso_code="fr", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="œàâçèéîïùûêŒÀÂÇÈÉÎÏÙÛÊ", - wiki_start_pages=["Wikipédia:Accueil_principal", "Bœuf (animal)"], - ), - "Hebrew": Language( - name="Hebrew", - iso_code="he", - use_ascii=False, - charsets=["ISO-8859-8", "WINDOWS-1255"], - alphabet="אבגדהוזחטיךכלםמןנסעףפץצקרשתװױײ", - wiki_start_pages=["עמוד_ראשי"], - ), - "Croatian": Language( - name="Croatian", - iso_code="hr", - # Q, W, X, Y are only used for foreign words. - use_ascii=False, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="abcčćdđefghijklmnoprsštuvzžABCČĆDĐEFGHIJKLMNOPRSŠTUVZŽ", - wiki_start_pages=["Glavna_stranica"], - ), - "Hungarian": Language( - name="Hungarian", - iso_code="hu", - # Q, W, X, Y are only used for foreign words. - use_ascii=False, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="abcdefghijklmnoprstuvzáéíóöőúüűABCDEFGHIJKLMNOPRSTUVZÁÉÍÓÖŐÚÜŰ", - wiki_start_pages=["Kezdőlap"], - ), - "Italian": Language( - name="Italian", - iso_code="it", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="ÀÈÉÌÒÓÙàèéìòóù", - wiki_start_pages=["Pagina_principale"], - ), - "Lithuanian": Language( - name="Lithuanian", - iso_code="lt", - use_ascii=False, - charsets=["ISO-8859-13", "WINDOWS-1257", "ISO-8859-4"], - # Q, W, and X not used at all - alphabet="AĄBCČDEĘĖFGHIĮYJKLMNOPRSŠTUŲŪVZŽaąbcčdeęėfghiįyjklmnoprsštuųūvzž", - wiki_start_pages=["Pagrindinis_puslapis"], - ), - "Latvian": Language( - name="Latvian", - iso_code="lv", - use_ascii=False, - charsets=["ISO-8859-13", "WINDOWS-1257", "ISO-8859-4"], - # Q, W, X, Y are only for loanwords - alphabet="AĀBCČDEĒFGĢHIĪJKĶLĻMNŅOPRSŠTUŪVZŽaābcčdeēfgģhiījkķlļmnņoprsštuūvzž", - wiki_start_pages=["Sākumlapa"], - ), - "Macedonian": Language( - name="Macedonian", - iso_code="mk", - use_ascii=False, - charsets=["ISO-8859-5", "WINDOWS-1251", "MacCyrillic", "IBM855"], - alphabet="АБВГДЃЕЖЗЅИЈКЛЉМНЊОПРСТЌУФХЦЧЏШабвгдѓежзѕијклљмнњопрстќуфхцчџш", - wiki_start_pages=["Главна_страница"], - ), - "Dutch": Language( - name="Dutch", - iso_code="nl", - use_ascii=True, - charsets=["ISO-8859-1", "WINDOWS-1252", "MacRoman"], - wiki_start_pages=["Hoofdpagina"], - ), - "Polish": Language( - name="Polish", - iso_code="pl", - # Q and X are only used for foreign words. - use_ascii=False, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="AĄBCĆDEĘFGHIJKLŁMNŃOÓPRSŚTUWYZŹŻaąbcćdeęfghijklłmnńoóprsśtuwyzźż", - wiki_start_pages=["Wikipedia:Strona_główna"], - ), - "Portuguese": Language( - name="Portuguese", - iso_code="pt", - use_ascii=True, - charsets=["ISO-8859-1", "ISO-8859-15", "WINDOWS-1252", "MacRoman"], - alphabet="ÁÂÃÀÇÉÊÍÓÔÕÚáâãàçéêíóôõú", - wiki_start_pages=["Wikipédia:Página_principal"], - ), - "Romanian": Language( - name="Romanian", - iso_code="ro", - use_ascii=True, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="ăâîșțĂÂÎȘȚ", - wiki_start_pages=["Pagina_principală"], - ), - "Russian": Language( - name="Russian", - iso_code="ru", - use_ascii=False, - charsets=[ - "ISO-8859-5", - "WINDOWS-1251", - "KOI8-R", - "MacCyrillic", - "IBM866", - "IBM855", - ], - alphabet="абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ", - wiki_start_pages=["Заглавная_страница"], - ), - "Slovak": Language( - name="Slovak", - iso_code="sk", - use_ascii=True, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="áäčďéíĺľňóôŕšťúýžÁÄČĎÉÍĹĽŇÓÔŔŠŤÚÝŽ", - wiki_start_pages=["Hlavná_stránka"], - ), - "Slovene": Language( - name="Slovene", - iso_code="sl", - # Q, W, X, Y are only used for foreign words. - use_ascii=False, - charsets=["ISO-8859-2", "WINDOWS-1250"], - alphabet="abcčdefghijklmnoprsštuvzžABCČDEFGHIJKLMNOPRSŠTUVZŽ", - wiki_start_pages=["Glavna_stran"], - ), - # Serbian can be written in both Latin and Cyrillic, but there's no - # simple way to get the Latin alphabet pages from Wikipedia through - # the API, so for now we just support Cyrillic. - "Serbian": Language( - name="Serbian", - iso_code="sr", - alphabet="АБВГДЂЕЖЗИЈКЛЉМНЊОПРСТЋУФХЦЧЏШабвгдђежзијклљмнњопрстћуфхцчџш", - charsets=["ISO-8859-5", "WINDOWS-1251", "MacCyrillic", "IBM855"], - wiki_start_pages=["Главна_страна"], - ), - "Thai": Language( - name="Thai", - iso_code="th", - use_ascii=False, - charsets=["ISO-8859-11", "TIS-620", "CP874"], - alphabet="กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรฤลฦวศษสหฬอฮฯะัาำิีึืฺุู฿เแโใไๅๆ็่้๊๋์ํ๎๏๐๑๒๓๔๕๖๗๘๙๚๛", - wiki_start_pages=["หน้าหลัก"], - ), - "Turkish": Language( - name="Turkish", - iso_code="tr", - # Q, W, and X are not used by Turkish - use_ascii=False, - charsets=["ISO-8859-3", "ISO-8859-9", "WINDOWS-1254"], - alphabet="abcçdefgğhıijklmnoöprsştuüvyzâîûABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZÂÎÛ", - wiki_start_pages=["Ana_Sayfa"], - ), - "Vietnamese": Language( - name="Vietnamese", - iso_code="vi", - use_ascii=False, - # Windows-1258 is the only common 8-bit - # Vietnamese encoding supported by Python. - # From Wikipedia: - # For systems that lack support for Unicode, - # dozens of 8-bit Vietnamese code pages are - # available.[1] The most common are VISCII - # (TCVN 5712:1993), VPS, and Windows-1258.[3] - # Where ASCII is required, such as when - # ensuring readability in plain text e-mail, - # Vietnamese letters are often encoded - # according to Vietnamese Quoted-Readable - # (VIQR) or VSCII Mnemonic (VSCII-MNEM),[4] - # though usage of either variable-width - # scheme has declined dramatically following - # the adoption of Unicode on the World Wide - # Web. - charsets=["WINDOWS-1258"], - alphabet="aăâbcdđeêghiklmnoôơpqrstuưvxyAĂÂBCDĐEÊGHIKLMNOÔƠPQRSTUƯVXY", - wiki_start_pages=["Chữ_Quốc_ngữ"], - ), -} diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/py.typed b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/py.typed deleted file mode 100644 index e69de29..0000000 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/resultdict.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/resultdict.py deleted file mode 100644 index 7d36e64..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/resultdict.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import TYPE_CHECKING, Optional - -if TYPE_CHECKING: - # TypedDict was introduced in Python 3.8. - # - # TODO: Remove the else block and TYPE_CHECKING check when dropping support - # for Python 3.7. - from typing import TypedDict - - class ResultDict(TypedDict): - encoding: Optional[str] - confidence: float - language: Optional[str] - -else: - ResultDict = dict diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sbcharsetprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sbcharsetprober.py deleted file mode 100644 index 0ffbcdd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sbcharsetprober.py +++ /dev/null @@ -1,162 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Dict, List, NamedTuple, Optional, Union - -from .charsetprober import CharSetProber -from .enums import CharacterCategory, ProbingState, SequenceLikelihood - - -class SingleByteCharSetModel(NamedTuple): - charset_name: str - language: str - char_to_order_map: Dict[int, int] - language_model: Dict[int, Dict[int, int]] - typical_positive_ratio: float - keep_ascii_letters: bool - alphabet: str - - -class SingleByteCharSetProber(CharSetProber): - SAMPLE_SIZE = 64 - SB_ENOUGH_REL_THRESHOLD = 1024 # 0.25 * SAMPLE_SIZE^2 - POSITIVE_SHORTCUT_THRESHOLD = 0.95 - NEGATIVE_SHORTCUT_THRESHOLD = 0.05 - - def __init__( - self, - model: SingleByteCharSetModel, - is_reversed: bool = False, - name_prober: Optional[CharSetProber] = None, - ) -> None: - super().__init__() - self._model = model - # TRUE if we need to reverse every pair in the model lookup - self._reversed = is_reversed - # Optional auxiliary prober for name decision - self._name_prober = name_prober - self._last_order = 255 - self._seq_counters: List[int] = [] - self._total_seqs = 0 - self._total_char = 0 - self._control_char = 0 - self._freq_char = 0 - self.reset() - - def reset(self) -> None: - super().reset() - # char order of last character - self._last_order = 255 - self._seq_counters = [0] * SequenceLikelihood.get_num_categories() - self._total_seqs = 0 - self._total_char = 0 - self._control_char = 0 - # characters that fall in our sampling range - self._freq_char = 0 - - @property - def charset_name(self) -> Optional[str]: - if self._name_prober: - return self._name_prober.charset_name - return self._model.charset_name - - @property - def language(self) -> Optional[str]: - if self._name_prober: - return self._name_prober.language - return self._model.language - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - # TODO: Make filter_international_words keep things in self.alphabet - if not self._model.keep_ascii_letters: - byte_str = self.filter_international_words(byte_str) - else: - byte_str = self.remove_xml_tags(byte_str) - if not byte_str: - return self.state - char_to_order_map = self._model.char_to_order_map - language_model = self._model.language_model - for char in byte_str: - order = char_to_order_map.get(char, CharacterCategory.UNDEFINED) - # XXX: This was SYMBOL_CAT_ORDER before, with a value of 250, but - # CharacterCategory.SYMBOL is actually 253, so we use CONTROL - # to make it closer to the original intent. The only difference - # is whether or not we count digits and control characters for - # _total_char purposes. - if order < CharacterCategory.CONTROL: - self._total_char += 1 - if order < self.SAMPLE_SIZE: - self._freq_char += 1 - if self._last_order < self.SAMPLE_SIZE: - self._total_seqs += 1 - if not self._reversed: - lm_cat = language_model[self._last_order][order] - else: - lm_cat = language_model[order][self._last_order] - self._seq_counters[lm_cat] += 1 - self._last_order = order - - charset_name = self._model.charset_name - if self.state == ProbingState.DETECTING: - if self._total_seqs > self.SB_ENOUGH_REL_THRESHOLD: - confidence = self.get_confidence() - if confidence > self.POSITIVE_SHORTCUT_THRESHOLD: - self.logger.debug( - "%s confidence = %s, we have a winner", charset_name, confidence - ) - self._state = ProbingState.FOUND_IT - elif confidence < self.NEGATIVE_SHORTCUT_THRESHOLD: - self.logger.debug( - "%s confidence = %s, below negative shortcut threshold %s", - charset_name, - confidence, - self.NEGATIVE_SHORTCUT_THRESHOLD, - ) - self._state = ProbingState.NOT_ME - - return self.state - - def get_confidence(self) -> float: - r = 0.01 - if self._total_seqs > 0: - r = ( - ( - self._seq_counters[SequenceLikelihood.POSITIVE] - + 0.25 * self._seq_counters[SequenceLikelihood.LIKELY] - ) - / self._total_seqs - / self._model.typical_positive_ratio - ) - # The more control characters (proportionnaly to the size - # of the text), the less confident we become in the current - # charset. - r = r * (self._total_char - self._control_char) / self._total_char - r = r * self._freq_char / self._total_char - if r >= 1.0: - r = 0.99 - return r diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sbcsgroupprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sbcsgroupprober.py deleted file mode 100644 index 890ae84..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sbcsgroupprober.py +++ /dev/null @@ -1,88 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from .charsetgroupprober import CharSetGroupProber -from .hebrewprober import HebrewProber -from .langbulgarianmodel import ISO_8859_5_BULGARIAN_MODEL, WINDOWS_1251_BULGARIAN_MODEL -from .langgreekmodel import ISO_8859_7_GREEK_MODEL, WINDOWS_1253_GREEK_MODEL -from .langhebrewmodel import WINDOWS_1255_HEBREW_MODEL - -# from .langhungarianmodel import (ISO_8859_2_HUNGARIAN_MODEL, -# WINDOWS_1250_HUNGARIAN_MODEL) -from .langrussianmodel import ( - IBM855_RUSSIAN_MODEL, - IBM866_RUSSIAN_MODEL, - ISO_8859_5_RUSSIAN_MODEL, - KOI8_R_RUSSIAN_MODEL, - MACCYRILLIC_RUSSIAN_MODEL, - WINDOWS_1251_RUSSIAN_MODEL, -) -from .langthaimodel import TIS_620_THAI_MODEL -from .langturkishmodel import ISO_8859_9_TURKISH_MODEL -from .sbcharsetprober import SingleByteCharSetProber - - -class SBCSGroupProber(CharSetGroupProber): - def __init__(self) -> None: - super().__init__() - hebrew_prober = HebrewProber() - logical_hebrew_prober = SingleByteCharSetProber( - WINDOWS_1255_HEBREW_MODEL, is_reversed=False, name_prober=hebrew_prober - ) - # TODO: See if using ISO-8859-8 Hebrew model works better here, since - # it's actually the visual one - visual_hebrew_prober = SingleByteCharSetProber( - WINDOWS_1255_HEBREW_MODEL, is_reversed=True, name_prober=hebrew_prober - ) - hebrew_prober.set_model_probers(logical_hebrew_prober, visual_hebrew_prober) - # TODO: ORDER MATTERS HERE. I changed the order vs what was in master - # and several tests failed that did not before. Some thought - # should be put into the ordering, and we should consider making - # order not matter here, because that is very counter-intuitive. - self.probers = [ - SingleByteCharSetProber(WINDOWS_1251_RUSSIAN_MODEL), - SingleByteCharSetProber(KOI8_R_RUSSIAN_MODEL), - SingleByteCharSetProber(ISO_8859_5_RUSSIAN_MODEL), - SingleByteCharSetProber(MACCYRILLIC_RUSSIAN_MODEL), - SingleByteCharSetProber(IBM866_RUSSIAN_MODEL), - SingleByteCharSetProber(IBM855_RUSSIAN_MODEL), - SingleByteCharSetProber(ISO_8859_7_GREEK_MODEL), - SingleByteCharSetProber(WINDOWS_1253_GREEK_MODEL), - SingleByteCharSetProber(ISO_8859_5_BULGARIAN_MODEL), - SingleByteCharSetProber(WINDOWS_1251_BULGARIAN_MODEL), - # TODO: Restore Hungarian encodings (iso-8859-2 and windows-1250) - # after we retrain model. - # SingleByteCharSetProber(ISO_8859_2_HUNGARIAN_MODEL), - # SingleByteCharSetProber(WINDOWS_1250_HUNGARIAN_MODEL), - SingleByteCharSetProber(TIS_620_THAI_MODEL), - SingleByteCharSetProber(ISO_8859_9_TURKISH_MODEL), - hebrew_prober, - logical_hebrew_prober, - visual_hebrew_prober, - ] - self.reset() diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sjisprober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sjisprober.py deleted file mode 100644 index 91df077..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/sjisprober.py +++ /dev/null @@ -1,105 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Union - -from .chardistribution import SJISDistributionAnalysis -from .codingstatemachine import CodingStateMachine -from .enums import MachineState, ProbingState -from .jpcntx import SJISContextAnalysis -from .mbcharsetprober import MultiByteCharSetProber -from .mbcssm import SJIS_SM_MODEL - - -class SJISProber(MultiByteCharSetProber): - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(SJIS_SM_MODEL) - self.distribution_analyzer = SJISDistributionAnalysis() - self.context_analyzer = SJISContextAnalysis() - self.reset() - - def reset(self) -> None: - super().reset() - self.context_analyzer.reset() - - @property - def charset_name(self) -> str: - return self.context_analyzer.charset_name - - @property - def language(self) -> str: - return "Japanese" - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - assert self.coding_sm is not None - assert self.distribution_analyzer is not None - - for i, byte in enumerate(byte_str): - coding_state = self.coding_sm.next_state(byte) - if coding_state == MachineState.ERROR: - self.logger.debug( - "%s %s prober hit error at byte %s", - self.charset_name, - self.language, - i, - ) - self._state = ProbingState.NOT_ME - break - if coding_state == MachineState.ITS_ME: - self._state = ProbingState.FOUND_IT - break - if coding_state == MachineState.START: - char_len = self.coding_sm.get_current_charlen() - if i == 0: - self._last_char[1] = byte - self.context_analyzer.feed( - self._last_char[2 - char_len :], char_len - ) - self.distribution_analyzer.feed(self._last_char, char_len) - else: - self.context_analyzer.feed( - byte_str[i + 1 - char_len : i + 3 - char_len], char_len - ) - self.distribution_analyzer.feed(byte_str[i - 1 : i + 1], char_len) - - self._last_char[0] = byte_str[-1] - - if self.state == ProbingState.DETECTING: - if self.context_analyzer.got_enough_data() and ( - self.get_confidence() > self.SHORTCUT_THRESHOLD - ): - self._state = ProbingState.FOUND_IT - - return self.state - - def get_confidence(self) -> float: - assert self.distribution_analyzer is not None - - context_conf = self.context_analyzer.get_confidence() - distrib_conf = self.distribution_analyzer.get_confidence() - return max(context_conf, distrib_conf) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/universaldetector.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/universaldetector.py deleted file mode 100644 index 30c441d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/universaldetector.py +++ /dev/null @@ -1,362 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is Mozilla Universal charset detector code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 2001 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# Shy Shalom - original C code -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### -""" -Module containing the UniversalDetector detector class, which is the primary -class a user of ``chardet`` should use. - -:author: Mark Pilgrim (initial port to Python) -:author: Shy Shalom (original C code) -:author: Dan Blanchard (major refactoring for 3.0) -:author: Ian Cordasco -""" - - -import codecs -import logging -import re -from typing import List, Optional, Union - -from .charsetgroupprober import CharSetGroupProber -from .charsetprober import CharSetProber -from .enums import InputState, LanguageFilter, ProbingState -from .escprober import EscCharSetProber -from .latin1prober import Latin1Prober -from .macromanprober import MacRomanProber -from .mbcsgroupprober import MBCSGroupProber -from .resultdict import ResultDict -from .sbcsgroupprober import SBCSGroupProber -from .utf1632prober import UTF1632Prober - - -class UniversalDetector: - """ - The ``UniversalDetector`` class underlies the ``chardet.detect`` function - and coordinates all of the different charset probers. - - To get a ``dict`` containing an encoding and its confidence, you can simply - run: - - .. code:: - - u = UniversalDetector() - u.feed(some_bytes) - u.close() - detected = u.result - - """ - - MINIMUM_THRESHOLD = 0.20 - HIGH_BYTE_DETECTOR = re.compile(b"[\x80-\xFF]") - ESC_DETECTOR = re.compile(b"(\033|~{)") - WIN_BYTE_DETECTOR = re.compile(b"[\x80-\x9F]") - ISO_WIN_MAP = { - "iso-8859-1": "Windows-1252", - "iso-8859-2": "Windows-1250", - "iso-8859-5": "Windows-1251", - "iso-8859-6": "Windows-1256", - "iso-8859-7": "Windows-1253", - "iso-8859-8": "Windows-1255", - "iso-8859-9": "Windows-1254", - "iso-8859-13": "Windows-1257", - } - # Based on https://encoding.spec.whatwg.org/#names-and-labels - # but altered to match Python names for encodings and remove mappings - # that break tests. - LEGACY_MAP = { - "ascii": "Windows-1252", - "iso-8859-1": "Windows-1252", - "tis-620": "ISO-8859-11", - "iso-8859-9": "Windows-1254", - "gb2312": "GB18030", - "euc-kr": "CP949", - "utf-16le": "UTF-16", - } - - def __init__( - self, - lang_filter: LanguageFilter = LanguageFilter.ALL, - should_rename_legacy: bool = False, - ) -> None: - self._esc_charset_prober: Optional[EscCharSetProber] = None - self._utf1632_prober: Optional[UTF1632Prober] = None - self._charset_probers: List[CharSetProber] = [] - self.result: ResultDict = { - "encoding": None, - "confidence": 0.0, - "language": None, - } - self.done = False - self._got_data = False - self._input_state = InputState.PURE_ASCII - self._last_char = b"" - self.lang_filter = lang_filter - self.logger = logging.getLogger(__name__) - self._has_win_bytes = False - self.should_rename_legacy = should_rename_legacy - self.reset() - - @property - def input_state(self) -> int: - return self._input_state - - @property - def has_win_bytes(self) -> bool: - return self._has_win_bytes - - @property - def charset_probers(self) -> List[CharSetProber]: - return self._charset_probers - - def reset(self) -> None: - """ - Reset the UniversalDetector and all of its probers back to their - initial states. This is called by ``__init__``, so you only need to - call this directly in between analyses of different documents. - """ - self.result = {"encoding": None, "confidence": 0.0, "language": None} - self.done = False - self._got_data = False - self._has_win_bytes = False - self._input_state = InputState.PURE_ASCII - self._last_char = b"" - if self._esc_charset_prober: - self._esc_charset_prober.reset() - if self._utf1632_prober: - self._utf1632_prober.reset() - for prober in self._charset_probers: - prober.reset() - - def feed(self, byte_str: Union[bytes, bytearray]) -> None: - """ - Takes a chunk of a document and feeds it through all of the relevant - charset probers. - - After calling ``feed``, you can check the value of the ``done`` - attribute to see if you need to continue feeding the - ``UniversalDetector`` more data, or if it has made a prediction - (in the ``result`` attribute). - - .. note:: - You should always call ``close`` when you're done feeding in your - document if ``done`` is not already ``True``. - """ - if self.done: - return - - if not byte_str: - return - - if not isinstance(byte_str, bytearray): - byte_str = bytearray(byte_str) - - # First check for known BOMs, since these are guaranteed to be correct - if not self._got_data: - # If the data starts with BOM, we know it is UTF - if byte_str.startswith(codecs.BOM_UTF8): - # EF BB BF UTF-8 with BOM - self.result = { - "encoding": "UTF-8-SIG", - "confidence": 1.0, - "language": "", - } - elif byte_str.startswith((codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE)): - # FF FE 00 00 UTF-32, little-endian BOM - # 00 00 FE FF UTF-32, big-endian BOM - self.result = {"encoding": "UTF-32", "confidence": 1.0, "language": ""} - elif byte_str.startswith(b"\xFE\xFF\x00\x00"): - # FE FF 00 00 UCS-4, unusual octet order BOM (3412) - self.result = { - # TODO: This encoding is not supported by Python. Should remove? - "encoding": "X-ISO-10646-UCS-4-3412", - "confidence": 1.0, - "language": "", - } - elif byte_str.startswith(b"\x00\x00\xFF\xFE"): - # 00 00 FF FE UCS-4, unusual octet order BOM (2143) - self.result = { - # TODO: This encoding is not supported by Python. Should remove? - "encoding": "X-ISO-10646-UCS-4-2143", - "confidence": 1.0, - "language": "", - } - elif byte_str.startswith((codecs.BOM_LE, codecs.BOM_BE)): - # FF FE UTF-16, little endian BOM - # FE FF UTF-16, big endian BOM - self.result = {"encoding": "UTF-16", "confidence": 1.0, "language": ""} - - self._got_data = True - if self.result["encoding"] is not None: - self.done = True - return - - # If none of those matched and we've only see ASCII so far, check - # for high bytes and escape sequences - if self._input_state == InputState.PURE_ASCII: - if self.HIGH_BYTE_DETECTOR.search(byte_str): - self._input_state = InputState.HIGH_BYTE - elif ( - self._input_state == InputState.PURE_ASCII - and self.ESC_DETECTOR.search(self._last_char + byte_str) - ): - self._input_state = InputState.ESC_ASCII - - self._last_char = byte_str[-1:] - - # next we will look to see if it is appears to be either a UTF-16 or - # UTF-32 encoding - if not self._utf1632_prober: - self._utf1632_prober = UTF1632Prober() - - if self._utf1632_prober.state == ProbingState.DETECTING: - if self._utf1632_prober.feed(byte_str) == ProbingState.FOUND_IT: - self.result = { - "encoding": self._utf1632_prober.charset_name, - "confidence": self._utf1632_prober.get_confidence(), - "language": "", - } - self.done = True - return - - # If we've seen escape sequences, use the EscCharSetProber, which - # uses a simple state machine to check for known escape sequences in - # HZ and ISO-2022 encodings, since those are the only encodings that - # use such sequences. - if self._input_state == InputState.ESC_ASCII: - if not self._esc_charset_prober: - self._esc_charset_prober = EscCharSetProber(self.lang_filter) - if self._esc_charset_prober.feed(byte_str) == ProbingState.FOUND_IT: - self.result = { - "encoding": self._esc_charset_prober.charset_name, - "confidence": self._esc_charset_prober.get_confidence(), - "language": self._esc_charset_prober.language, - } - self.done = True - # If we've seen high bytes (i.e., those with values greater than 127), - # we need to do more complicated checks using all our multi-byte and - # single-byte probers that are left. The single-byte probers - # use character bigram distributions to determine the encoding, whereas - # the multi-byte probers use a combination of character unigram and - # bigram distributions. - elif self._input_state == InputState.HIGH_BYTE: - if not self._charset_probers: - self._charset_probers = [MBCSGroupProber(self.lang_filter)] - # If we're checking non-CJK encodings, use single-byte prober - if self.lang_filter & LanguageFilter.NON_CJK: - self._charset_probers.append(SBCSGroupProber()) - self._charset_probers.append(Latin1Prober()) - self._charset_probers.append(MacRomanProber()) - for prober in self._charset_probers: - if prober.feed(byte_str) == ProbingState.FOUND_IT: - self.result = { - "encoding": prober.charset_name, - "confidence": prober.get_confidence(), - "language": prober.language, - } - self.done = True - break - if self.WIN_BYTE_DETECTOR.search(byte_str): - self._has_win_bytes = True - - def close(self) -> ResultDict: - """ - Stop analyzing the current document and come up with a final - prediction. - - :returns: The ``result`` attribute, a ``dict`` with the keys - `encoding`, `confidence`, and `language`. - """ - # Don't bother with checks if we're already done - if self.done: - return self.result - self.done = True - - if not self._got_data: - self.logger.debug("no data received!") - - # Default to ASCII if it is all we've seen so far - elif self._input_state == InputState.PURE_ASCII: - self.result = {"encoding": "ascii", "confidence": 1.0, "language": ""} - - # If we have seen non-ASCII, return the best that met MINIMUM_THRESHOLD - elif self._input_state == InputState.HIGH_BYTE: - prober_confidence = None - max_prober_confidence = 0.0 - max_prober = None - for prober in self._charset_probers: - if not prober: - continue - prober_confidence = prober.get_confidence() - if prober_confidence > max_prober_confidence: - max_prober_confidence = prober_confidence - max_prober = prober - if max_prober and (max_prober_confidence > self.MINIMUM_THRESHOLD): - charset_name = max_prober.charset_name - assert charset_name is not None - lower_charset_name = charset_name.lower() - confidence = max_prober.get_confidence() - # Use Windows encoding name instead of ISO-8859 if we saw any - # extra Windows-specific bytes - if lower_charset_name.startswith("iso-8859"): - if self._has_win_bytes: - charset_name = self.ISO_WIN_MAP.get( - lower_charset_name, charset_name - ) - # Rename legacy encodings with superset encodings if asked - if self.should_rename_legacy: - charset_name = self.LEGACY_MAP.get( - (charset_name or "").lower(), charset_name - ) - self.result = { - "encoding": charset_name, - "confidence": confidence, - "language": max_prober.language, - } - - # Log all prober confidences if none met MINIMUM_THRESHOLD - if self.logger.getEffectiveLevel() <= logging.DEBUG: - if self.result["encoding"] is None: - self.logger.debug("no probers hit minimum threshold") - for group_prober in self._charset_probers: - if not group_prober: - continue - if isinstance(group_prober, CharSetGroupProber): - for prober in group_prober.probers: - self.logger.debug( - "%s %s confidence = %s", - prober.charset_name, - prober.language, - prober.get_confidence(), - ) - else: - self.logger.debug( - "%s %s confidence = %s", - group_prober.charset_name, - group_prober.language, - group_prober.get_confidence(), - ) - return self.result diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/utf1632prober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/utf1632prober.py deleted file mode 100644 index 6bdec63..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/utf1632prober.py +++ /dev/null @@ -1,225 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# -# Contributor(s): -# Jason Zavaglia -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### -from typing import List, Union - -from .charsetprober import CharSetProber -from .enums import ProbingState - - -class UTF1632Prober(CharSetProber): - """ - This class simply looks for occurrences of zero bytes, and infers - whether the file is UTF16 or UTF32 (low-endian or big-endian) - For instance, files looking like ( \0 \0 \0 [nonzero] )+ - have a good probability to be UTF32BE. Files looking like ( \0 [nonzero] )+ - may be guessed to be UTF16BE, and inversely for little-endian varieties. - """ - - # how many logical characters to scan before feeling confident of prediction - MIN_CHARS_FOR_DETECTION = 20 - # a fixed constant ratio of expected zeros or non-zeros in modulo-position. - EXPECTED_RATIO = 0.94 - - def __init__(self) -> None: - super().__init__() - self.position = 0 - self.zeros_at_mod = [0] * 4 - self.nonzeros_at_mod = [0] * 4 - self._state = ProbingState.DETECTING - self.quad = [0, 0, 0, 0] - self.invalid_utf16be = False - self.invalid_utf16le = False - self.invalid_utf32be = False - self.invalid_utf32le = False - self.first_half_surrogate_pair_detected_16be = False - self.first_half_surrogate_pair_detected_16le = False - self.reset() - - def reset(self) -> None: - super().reset() - self.position = 0 - self.zeros_at_mod = [0] * 4 - self.nonzeros_at_mod = [0] * 4 - self._state = ProbingState.DETECTING - self.invalid_utf16be = False - self.invalid_utf16le = False - self.invalid_utf32be = False - self.invalid_utf32le = False - self.first_half_surrogate_pair_detected_16be = False - self.first_half_surrogate_pair_detected_16le = False - self.quad = [0, 0, 0, 0] - - @property - def charset_name(self) -> str: - if self.is_likely_utf32be(): - return "utf-32be" - if self.is_likely_utf32le(): - return "utf-32le" - if self.is_likely_utf16be(): - return "utf-16be" - if self.is_likely_utf16le(): - return "utf-16le" - # default to something valid - return "utf-16" - - @property - def language(self) -> str: - return "" - - def approx_32bit_chars(self) -> float: - return max(1.0, self.position / 4.0) - - def approx_16bit_chars(self) -> float: - return max(1.0, self.position / 2.0) - - def is_likely_utf32be(self) -> bool: - approx_chars = self.approx_32bit_chars() - return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( - self.zeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO - and self.zeros_at_mod[1] / approx_chars > self.EXPECTED_RATIO - and self.zeros_at_mod[2] / approx_chars > self.EXPECTED_RATIO - and self.nonzeros_at_mod[3] / approx_chars > self.EXPECTED_RATIO - and not self.invalid_utf32be - ) - - def is_likely_utf32le(self) -> bool: - approx_chars = self.approx_32bit_chars() - return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( - self.nonzeros_at_mod[0] / approx_chars > self.EXPECTED_RATIO - and self.zeros_at_mod[1] / approx_chars > self.EXPECTED_RATIO - and self.zeros_at_mod[2] / approx_chars > self.EXPECTED_RATIO - and self.zeros_at_mod[3] / approx_chars > self.EXPECTED_RATIO - and not self.invalid_utf32le - ) - - def is_likely_utf16be(self) -> bool: - approx_chars = self.approx_16bit_chars() - return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( - (self.nonzeros_at_mod[1] + self.nonzeros_at_mod[3]) / approx_chars - > self.EXPECTED_RATIO - and (self.zeros_at_mod[0] + self.zeros_at_mod[2]) / approx_chars - > self.EXPECTED_RATIO - and not self.invalid_utf16be - ) - - def is_likely_utf16le(self) -> bool: - approx_chars = self.approx_16bit_chars() - return approx_chars >= self.MIN_CHARS_FOR_DETECTION and ( - (self.nonzeros_at_mod[0] + self.nonzeros_at_mod[2]) / approx_chars - > self.EXPECTED_RATIO - and (self.zeros_at_mod[1] + self.zeros_at_mod[3]) / approx_chars - > self.EXPECTED_RATIO - and not self.invalid_utf16le - ) - - def validate_utf32_characters(self, quad: List[int]) -> None: - """ - Validate if the quad of bytes is valid UTF-32. - - UTF-32 is valid in the range 0x00000000 - 0x0010FFFF - excluding 0x0000D800 - 0x0000DFFF - - https://en.wikipedia.org/wiki/UTF-32 - """ - if ( - quad[0] != 0 - or quad[1] > 0x10 - or (quad[0] == 0 and quad[1] == 0 and 0xD8 <= quad[2] <= 0xDF) - ): - self.invalid_utf32be = True - if ( - quad[3] != 0 - or quad[2] > 0x10 - or (quad[3] == 0 and quad[2] == 0 and 0xD8 <= quad[1] <= 0xDF) - ): - self.invalid_utf32le = True - - def validate_utf16_characters(self, pair: List[int]) -> None: - """ - Validate if the pair of bytes is valid UTF-16. - - UTF-16 is valid in the range 0x0000 - 0xFFFF excluding 0xD800 - 0xFFFF - with an exception for surrogate pairs, which must be in the range - 0xD800-0xDBFF followed by 0xDC00-0xDFFF - - https://en.wikipedia.org/wiki/UTF-16 - """ - if not self.first_half_surrogate_pair_detected_16be: - if 0xD8 <= pair[0] <= 0xDB: - self.first_half_surrogate_pair_detected_16be = True - elif 0xDC <= pair[0] <= 0xDF: - self.invalid_utf16be = True - else: - if 0xDC <= pair[0] <= 0xDF: - self.first_half_surrogate_pair_detected_16be = False - else: - self.invalid_utf16be = True - - if not self.first_half_surrogate_pair_detected_16le: - if 0xD8 <= pair[1] <= 0xDB: - self.first_half_surrogate_pair_detected_16le = True - elif 0xDC <= pair[1] <= 0xDF: - self.invalid_utf16le = True - else: - if 0xDC <= pair[1] <= 0xDF: - self.first_half_surrogate_pair_detected_16le = False - else: - self.invalid_utf16le = True - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - for c in byte_str: - mod4 = self.position % 4 - self.quad[mod4] = c - if mod4 == 3: - self.validate_utf32_characters(self.quad) - self.validate_utf16_characters(self.quad[0:2]) - self.validate_utf16_characters(self.quad[2:4]) - if c == 0: - self.zeros_at_mod[mod4] += 1 - else: - self.nonzeros_at_mod[mod4] += 1 - self.position += 1 - return self.state - - @property - def state(self) -> ProbingState: - if self._state in {ProbingState.NOT_ME, ProbingState.FOUND_IT}: - # terminal, decided states - return self._state - if self.get_confidence() > 0.80: - self._state = ProbingState.FOUND_IT - elif self.position > 4 * 1024: - # if we get to 4kb into the file, and we can't conclude it's UTF, - # let's give up - self._state = ProbingState.NOT_ME - return self._state - - def get_confidence(self) -> float: - return ( - 0.85 - if ( - self.is_likely_utf16le() - or self.is_likely_utf16be() - or self.is_likely_utf32le() - or self.is_likely_utf32be() - ) - else 0.00 - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/utf8prober.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/utf8prober.py deleted file mode 100644 index d96354d..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/utf8prober.py +++ /dev/null @@ -1,82 +0,0 @@ -######################## BEGIN LICENSE BLOCK ######################## -# The Original Code is mozilla.org code. -# -# The Initial Developer of the Original Code is -# Netscape Communications Corporation. -# Portions created by the Initial Developer are Copyright (C) 1998 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Mark Pilgrim - port to Python -# -# This library is free software; you can redistribute it and/or -# modify it under the terms of the GNU Lesser General Public -# License as published by the Free Software Foundation; either -# version 2.1 of the License, or (at your option) any later version. -# -# This library is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public -# License along with this library; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -# 02110-1301 USA -######################### END LICENSE BLOCK ######################### - -from typing import Union - -from .charsetprober import CharSetProber -from .codingstatemachine import CodingStateMachine -from .enums import MachineState, ProbingState -from .mbcssm import UTF8_SM_MODEL - - -class UTF8Prober(CharSetProber): - ONE_CHAR_PROB = 0.5 - - def __init__(self) -> None: - super().__init__() - self.coding_sm = CodingStateMachine(UTF8_SM_MODEL) - self._num_mb_chars = 0 - self.reset() - - def reset(self) -> None: - super().reset() - self.coding_sm.reset() - self._num_mb_chars = 0 - - @property - def charset_name(self) -> str: - return "utf-8" - - @property - def language(self) -> str: - return "" - - def feed(self, byte_str: Union[bytes, bytearray]) -> ProbingState: - for c in byte_str: - coding_state = self.coding_sm.next_state(c) - if coding_state == MachineState.ERROR: - self._state = ProbingState.NOT_ME - break - if coding_state == MachineState.ITS_ME: - self._state = ProbingState.FOUND_IT - break - if coding_state == MachineState.START: - if self.coding_sm.get_current_charlen() >= 2: - self._num_mb_chars += 1 - - if self.state == ProbingState.DETECTING: - if self.get_confidence() > self.SHORTCUT_THRESHOLD: - self._state = ProbingState.FOUND_IT - - return self.state - - def get_confidence(self) -> float: - unlike = 0.99 - if self._num_mb_chars < 6: - unlike *= self.ONE_CHAR_PROB**self._num_mb_chars - return 1.0 - unlike - return unlike diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/version.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/version.py deleted file mode 100644 index 19dd01e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/chardet/version.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -This module exists only to simplify retrieving the version number of chardet -from within setuptools and from chardet subpackages. - -:author: Dan Blanchard (dan.blanchard@gmail.com) -""" - -__version__ = "5.2.0" -VERSION = __version__.split(".") diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/__init__.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/__init__.py deleted file mode 100644 index ef794bd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -""":mod:`wand` --- Simple `MagickWand API`_ binding for Python -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. _MagickWand API: http://www.imagemagick.org/script/magick-wand.php - -""" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/api.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/api.py deleted file mode 100644 index e334006..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/api.py +++ /dev/null @@ -1,245 +0,0 @@ -""":mod:`wand.api` --- Low-level interfaces -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionchanged:: 0.1.10 - Changed to throw :exc:`~exceptions.ImportError` instead of - :exc:`~exceptions.AttributeError` when the shared library fails to load. - -""" -import ctypes -import ctypes.util -import itertools -import os -import os.path -import platform -import sys -import traceback -# Forward import for backwards compatibility. -from .cdefs.structures import (AffineMatrix, MagickPixelPacket, PixelInfo, - PointInfo) -if platform.system() == "Windows": - try: - import winreg - except ImportError: - import _winreg as winreg - -__all__ = ('AffineMatrix', 'MagickPixelPacket', 'library', 'libc', 'libmagick', - 'load_library', 'PixelInfo', 'PointInfo') - - -def library_paths(): - """Iterates for library paths to try loading. The result paths are not - guaranteed that they exist. - - :returns: a pair of libwand and libmagick paths. they can be the same. - path can be ``None`` as well - :rtype: :class:`tuple` - - """ - libwand = None - libmagick = None - versions = '', '-7', '-7.Q8', '-7.Q16', '-6', '-Q16', '-Q8', '-6.Q16' - options = '', 'HDRI', 'HDRI-2' - system = platform.system() - magick_home = os.environ.get('MAGICK_HOME') - magick_suffix = os.environ.get('WAND_MAGICK_LIBRARY_SUFFIX') - - if system == 'Windows': - # ImageMagick installers normally install coder and filter DLLs in - # subfolders, we need to add those folders to PATH, otherwise loading - # the DLL later will fail. - try: - with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, - r"SOFTWARE\ImageMagick\Current") as reg_key: - libPath = winreg.QueryValueEx(reg_key, "LibPath") - coderPath = winreg.QueryValueEx(reg_key, "CoderModulesPath") - filterPath = winreg.QueryValueEx(reg_key, "FilterModulesPath") - magick_home = libPath[0] - os.environ['PATH'] += str((';' + libPath[0] + ";" + - coderPath[0] + ";" + filterPath[0])) - except OSError: - # otherwise use MAGICK_HOME, and we assume the coder and - # filter DLLs are in the same directory - pass - - def magick_path(path): - return os.path.join(magick_home, *path) - combinations = itertools.product(versions, options) - suffixes = list() - if magick_suffix: - suffixes = str(magick_suffix).split(';') - # We need to convert the ``combinations`` generator to a list so we can - # iterate over it twice. - suffixes.extend(list(version + option for version, option in combinations)) - if magick_home: - # exhaustively search for libraries in magick_home before calling - # find_library. - for suffix in suffixes: - # On Windows, the API is split between two libs. On other - # platforms, it's all contained in one. - if system == 'Windows': - libwand = 'CORE_RL_wand_{0}.dll'.format(suffix), - libmagick = 'CORE_RL_magick_{0}.dll'.format(suffix), - yield magick_path(libwand), magick_path(libmagick) - libwand = 'CORE_RL_MagickWand_{0}.dll'.format(suffix), - libmagick = 'CORE_RL_MagickCore_{0}.dll'.format(suffix), - yield magick_path(libwand), magick_path(libmagick) - libwand = 'libMagickWand{0}.dll'.format(suffix), - libmagick = 'libMagickCore{0}.dll'.format(suffix), - yield magick_path(libwand), magick_path(libmagick) - elif system == 'Darwin': - libwand = 'lib', 'libMagickWand{0}.dylib'.format(suffix), - yield magick_path(libwand), magick_path(libwand) - else: - libwand = 'lib', 'libMagickWand{0}.so'.format(suffix), - libmagick = 'lib', 'libMagickCore{0}.so'.format(suffix), - yield magick_path(libwand), magick_path(libmagick) - libwand = 'lib', 'libMagickWand{0}.so.9'.format(suffix), - libmagick = 'lib', 'libMagickCore{0}.so.9'.format(suffix), - yield magick_path(libwand), magick_path(libmagick) - libwand = 'lib', 'libMagickWand{0}.so.6'.format(suffix), - libmagick = 'lib', 'libMagickCore{0}.so.6'.format(suffix), - yield magick_path(libwand), magick_path(libmagick) - for suffix in suffixes: - if system == 'Windows': - libwand = ctypes.util.find_library('CORE_RL_wand_' + suffix) - libmagick = ctypes.util.find_library('CORE_RL_magick_' + suffix) - yield libwand, libmagick - libwand = ctypes.util.find_library('CORE_RL_MagickWand_' + suffix) - libmagick = ctypes.util.find_library( - 'CORE_RL_MagickCore_' + suffix - ) - yield libwand, libmagick - libwand = ctypes.util.find_library('libMagickWand' + suffix) - libmagick = ctypes.util.find_library('libMagickCore' + suffix) - yield libwand, libmagick - else: - libwand = ctypes.util.find_library('MagickWand' + suffix) - yield libwand, libwand - - -def load_library(): - """Loads the MagickWand library. - - :returns: the MagickWand library and the ImageMagick library - :rtype: :class:`ctypes.CDLL` - - """ - tried_paths = [] - for libwand_path, libmagick_path in library_paths(): - if libwand_path is None or libmagick_path is None: - continue - try: - tried_paths.append(libwand_path) - libwand = ctypes.CDLL(str(libwand_path)) - if libwand_path == libmagick_path: - libmagick = libwand - else: - tried_paths.append(libmagick_path) - libmagick = ctypes.CDLL(str(libmagick_path)) - except (IOError, OSError): - continue - return libwand, libmagick - raise IOError('cannot find library; tried paths: ' + repr(tried_paths)) - - -try: - # Preserve the module itself even if it fails to import - sys.modules['wand._api'] = sys.modules['wand.api'] -except KeyError: - # Loading the module locally or a non-standard setting - pass - -try: - libraries = load_library() -except (OSError, IOError): - msg = 'https://docs.wand-py.org/en/latest/guide/install.html' - if sys.platform.startswith(('dragonfly', 'freebsd')): - msg = 'pkg install' - elif sys.platform == 'win32': - msg += '#install-imagemagick-on-windows' - elif sys.platform == 'darwin': - mac_pkgmgrs = {'brew': 'brew install freetype imagemagick', - 'port': 'port install imagemagick'} - for pkgmgr in mac_pkgmgrs: - with os.popen('which ' + pkgmgr) as f: - if f.read().strip(): - msg = mac_pkgmgrs[pkgmgr] - break - else: - msg += '#install-imagemagick-on-mac' - elif hasattr(platform, 'linux_distribution'): - distname, _, __ = platform.linux_distribution() - distname = (distname or '').lower() - if distname in ('debian', 'ubuntu'): - msg = 'apt-get install libmagickwand-dev' - elif distname in ('fedora', 'centos', 'redhat'): - msg = 'yum install ImageMagick-devel' - raise ImportError('MagickWand shared library not found.\n' - 'You probably had not installed ImageMagick library.\n' - 'Try to install:\n ' + msg) - -#: (:class:`ctypes.CDLL`) The MagickWand library. -library = libraries[0] - -#: (:class:`ctypes.CDLL`) The ImageMagick library. It is the same with -#: :data:`library` on platforms other than Windows. -#: -#: .. versionadded:: 0.1.10 -libmagick = libraries[1] - -try: - from wand.cdefs import (core, magick_wand, magick_image, magick_property, - pixel_iterator, pixel_wand, drawing_wand) - - core.load(libmagick) - # Let's get the magick-version number to pass to load methods. - IM_VERSION = ctypes.c_size_t() - libmagick.GetMagickVersion(ctypes.byref(IM_VERSION)) - # Query Quantum Depth (i.e. Q8, Q16, ... etc). - IM_QUANTUM_DEPTH = ctypes.c_size_t() - libmagick.GetMagickQuantumDepth(ctypes.byref(IM_QUANTUM_DEPTH)) - # Does the library support HDRI? - IM_HDRI = 'HDRI' in str(libmagick.GetMagickFeatures()) - core.load_with_version(libmagick, IM_VERSION.value) - magick_wand.load(library, IM_VERSION.value) - magick_property.load(library, IM_VERSION.value) - magick_image.load(library, IM_VERSION.value) - pixel_iterator.load(library, IM_VERSION.value) - pixel_wand.load(library, IM_VERSION.value, IM_QUANTUM_DEPTH.value, IM_HDRI) - drawing_wand.load(library, IM_VERSION.value) - del IM_HDRI, IM_QUANTUM_DEPTH, IM_VERSION - -except AttributeError: - raise ImportError('MagickWand shared library not found or incompatible\n' - 'Original exception was raised in:\n' + - traceback.format_exc()) - -#: (:class:`ctypes.CDLL`) The C standard library. -libc = None - -if platform.system() == 'Windows': - msvcrt = ctypes.util.find_msvcrt() - # workaround -- the newest visual studio DLL is named differently: - if not msvcrt and '1900' in platform.python_compiler(): - msvcrt = 'vcruntime140.dll' - if msvcrt: - libc = ctypes.CDLL(msvcrt) -else: - libc_path = ctypes.util.find_library('c') - if libc_path: - libc = ctypes.cdll.LoadLibrary(libc_path) - else: - # Attempt to guess popular versions of libc - libc_paths = ('libc.so.6', 'libc.so', 'libc.a', 'libc.dylib', - '/usr/lib/libc.dylib') - for libc_path in libc_paths: - try: - libc = ctypes.cdll.LoadLibrary(libc_path) - break - except (IOError, OSError): - continue - if libc: - libc.fdopen.argtypes = [ctypes.c_int, ctypes.c_char_p] - libc.fdopen.restype = ctypes.c_void_p - libc.fflush.argtypes = [ctypes.c_void_p] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/assertions.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/assertions.py deleted file mode 100644 index 4702df2..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/assertions.py +++ /dev/null @@ -1,155 +0,0 @@ -""":mod:`wand.assertions` --- Input assertion helpers -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This module checks user input before calling MagickWands C-API methods. - - -.. versionadded:: 0.5.4 -""" - -import numbers -try: - from collections.abc import Sequence -except ImportError: - from collections import Sequence - -from .compat import string_type - - -def assert_bool(**kwargs): - """Ensure all given values are boolean. - - :raises TypeError: if value is not ``True`` or ``False. - - .. versionadded:: 0.5.4 - """ - for label, subject in kwargs.items(): - if not isinstance(subject, bool): - fmt = "{0} must be a bool, not {1}" - msg = fmt.format(label, repr(subject)) - raise TypeError(msg) - - -def assert_color(**kwargs): - """Ensure all given values are instances of :class:`~wand.color.Color`. - - :raises TypeError: if value is not :class:`~wand.color.Color`. - - .. versionadded:: 0.5.4 - """ - for label, subject in kwargs.items(): - if not isinstance(subject, Color): - fmt = "Expecting an instance of wand.color.Color for {0}, not {1}" - msg = fmt.format(label, repr(subject)) - raise TypeError(msg) - - -def assert_counting_number(**kwargs): - """Ensure all given values are natural integer. - - :raises TypeError: if value is not an integer. - :raises ValueError: if value is less than ``1``. - - .. versionadded:: 0.5.4 - """ - assert_integer(**kwargs) - for label, subject in kwargs.items(): - if subject < 1: - fmt = "{0}={1} must be an natural number greater than 0" - msg = fmt.format(label, subject) - raise ValueError(msg) - - -def assert_integer(**kwargs): - """Ensure all given values are an integer. - - :raises TypeError: if value is not an integer. - - .. versionadded:: 0.5.4 - """ - for label, subject in kwargs.items(): - if not isinstance(subject, numbers.Integral): - fmt = "{0} must be an integer, not {1}" - msg = fmt.format(label, repr(subject)) - raise TypeError(msg) - - -def assert_real(**kwargs): - """Ensure all given values are real numbers. - - :raises TypeError: if value is not a real number. - - .. versionadded:: 0.5.4 - """ - for label, subject in kwargs.items(): - if not isinstance(subject, numbers.Real): - fmt = "{0} must be a real number, not {1}" - msg = fmt.format(label, repr(subject)) - raise TypeError(msg) - - -def assert_unsigned_integer(**kwargs): - """Ensure all given values are positive integer. - - :raises TypeError: if value is not an integer. - :raises ValueError: if value is less than ``0``. - - .. versionadded:: 0.5.4 - """ - assert_integer(**kwargs) - for label, subject in kwargs.items(): - if subject < 0: - fmt = "{0}={1} must be a positive integer" - msg = fmt.format(label, subject) - raise ValueError(msg) - - -def assert_coordinate(**kwargs): - """Ensure all given values are a sequence of 2 real numbers. - - :raises TypeError: if value is not a pair of doubles. - - .. versionadded:: 0.6.0 - """ - for label, subject in kwargs.items(): - if not isinstance(subject, Sequence): - fmt = "'{0}' must be a pair of real numbers, not {1}" - msg = fmt.format(label, repr(subject)) - raise TypeError(msg) - elif len(subject) != 2: - fmt = "'{0}' must be a exactly 2 real numbers, not {1}" - msg = fmt.format(label, len(subject)) - raise ValueError(msg) - elif not isinstance(subject[0], numbers.Real): - fmt = "first entry of '{0}' must be a real number, not {1}" - msg = fmt.format(label, repr(subject[0])) - raise TypeError(msg) - elif not isinstance(subject[1], numbers.Real): - fmt = "second entry of '{0}' must be a real number, not {1}" - msg = fmt.format(label, repr(subject[1])) - raise TypeError(msg) - - -def assert_string(**kwargs): - for label, subject in kwargs.items(): - if not isinstance(subject, string_type): - fmt = "{0} must be a string, not {1}" - msg = fmt.format(label, repr(subject)) - raise TypeError(msg) - - -def in_list(options, label, **kwargs): - for subject_label, subject in kwargs.items(): - if subject not in options: - fmt = "{0} must be defined in {1}, not {2}" - msg = fmt.format(subject_label, label, repr(subject)) - raise ValueError(msg) - - -def string_in_list(options, label, **kwargs): - assert_string(**kwargs) - in_list(options, label, **kwargs) - - -# Lazy load recursive import -from .color import Color # noqa: E402 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/__init__.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/__init__.py deleted file mode 100644 index ee55abd..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -""":mod:`wand.cdefs` --- Low-level definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/core.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/core.py deleted file mode 100644 index c3faf90..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/core.py +++ /dev/null @@ -1,135 +0,0 @@ -""":mod:`wand.cdefs.core` --- MagickCore definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import (POINTER, c_void_p, c_char_p, c_int, c_size_t, - c_ulonglong, c_bool) -from wand.cdefs.wandtypes import c_ssize_t - -__all__ = ('load', 'load_with_version') - - -def load(libmagick): - """Define MagickCore methods. - We'll only define the bare-minimum methods to support the MagickWand - library. - - .. seealso:: - - #include - // Or - #include - - :param libmagick: the loaded ``MagickCore`` library. - :type libmagick: :class:`ctypes.CDLL` - - .. versionadded:: 0.5.0 - - """ - libmagick.AcquireExceptionInfo.argtypes = [] - libmagick.AcquireExceptionInfo.restype = c_void_p - libmagick.AcquireImageInfo.argtypes = [] - libmagick.AcquireImageInfo.restype = c_void_p - libmagick.CloneImageInfo.argtypes = [c_void_p] - libmagick.CloneImageInfo.restype = c_void_p - libmagick.CloneImages.argtypes = [c_void_p, c_char_p, c_void_p] - libmagick.CloneImages.restype = c_void_p - libmagick.DestroyExceptionInfo.argtypes = [c_void_p] - libmagick.DestroyExceptionInfo.restype = c_void_p - libmagick.DestroyImage.argtypes = [c_void_p] - libmagick.DestroyImage.restype = c_void_p - libmagick.DestroyImageInfo.argtypes = [c_void_p] - libmagick.DestroyImageInfo.restype = c_void_p - libmagick.DestroyString.argtypes = [c_void_p] - libmagick.DestroyString.restype = c_void_p - try: - libmagick.GetGeometry.argtypes = [c_char_p, - POINTER(c_ssize_t), - POINTER(c_ssize_t), - POINTER(c_size_t), - POINTER(c_size_t)] - libmagick.GetGeometry.restype = c_int - except AttributeError: - libmagick.GetGeometry = None - libmagick.GetMagickCopyright.argtypes = [] - libmagick.GetMagickCopyright.restype = c_char_p - try: - libmagick.GetMagickDelegates.argtypes = [] - libmagick.GetMagickDelegates.restype = c_char_p - except AttributeError: - libmagick.GetMagickDelegates = None - libmagick.GetMagickFeatures.argtypes = [] - libmagick.GetMagickFeatures.restype = c_char_p - try: - libmagick.GetMagickLicense.argtypes = [] - libmagick.GetMagickLicense.restype = c_char_p - except AttributeError: - pass - libmagick.GetMagickPackageName.argtypes = [] - libmagick.GetMagickPackageName.restype = c_char_p - libmagick.GetMagickQuantumDepth.argtypes = [POINTER(c_size_t)] - libmagick.GetMagickQuantumDepth.restype = c_char_p - libmagick.GetMagickQuantumRange.argtypes = [POINTER(c_size_t)] - libmagick.GetMagickQuantumRange.restype = c_char_p - libmagick.GetMagickReleaseDate.argtypes = [] - libmagick.GetMagickReleaseDate.restype = c_char_p - libmagick.GetMagickResource.argtypes = [c_int] - libmagick.GetMagickResource.restype = c_ulonglong - libmagick.GetMagickResourceLimit.argtypes = [c_int] - libmagick.GetMagickResourceLimit.restype = c_ulonglong - libmagick.GetMagickVersion.argtypes = [POINTER(c_size_t)] - libmagick.GetMagickVersion.restype = c_char_p - try: - libmagick.GetPageGeometry.argtypes = [c_char_p] - libmagick.GetPageGeometry.restype = c_void_p - except AttributeError: - libmagick.GetPageGeometry = None - libmagick.GetNextImageInList.argtypes = [c_void_p] - libmagick.GetNextImageInList.restype = c_void_p - libmagick.MagickToMime.argtypes = [c_char_p] - libmagick.MagickToMime.restype = c_void_p - try: - libmagick.ParseAbsoluteGeometry.argtypes = [c_char_p, c_void_p] - libmagick.ParseAbsoluteGeometry.restype = c_int - except AttributeError: - libmagick.ParseAbsoluteGeometry = None - try: - libmagick.ParseChannelOption.argtypes = [c_char_p] - libmagick.ParseChannelOption.restypes = c_ssize_t - except AttributeError: - libmagick.ParseChannelOption = None - try: - libmagick.ParseGeometry.argtypes = [c_char_p, c_void_p] - libmagick.ParseGeometry.restype = c_int - libmagick.ParseMetaGeometry.argtypes = [c_char_p, - POINTER(c_ssize_t), - POINTER(c_ssize_t), - POINTER(c_size_t), - POINTER(c_size_t)] - libmagick.ParseMetaGeometry.restype = c_int - except AttributeError: - libmagick.ParseGeometry = None - libmagick.ParseMetaGeometry = None - libmagick.SetImageOption.argtypes = [c_void_p, c_char_p, c_char_p] - libmagick.SetImageOption.restype = c_bool - libmagick.SetMagickResourceLimit.argtypes = [c_int, c_ulonglong] - libmagick.SetMagickResourceLimit.restype = c_int - libmagick.SyncImageSettings.argtypes = [c_void_p, c_void_p, c_void_p] - libmagick.SyncImageSettings.restype = c_bool - - -def load_with_version(libmagick, IM_VERSION): - if IM_VERSION < 0x700: - libmagick.AcquireKernelBuiltIn.argtypes = [c_int, c_void_p] - libmagick.AcquireKernelBuiltIn.restype = c_void_p - libmagick.AcquireKernelInfo.argtypes = [c_char_p] - libmagick.AcquireKernelInfo.restype = c_void_p - else: - libmagick.AcquireKernelBuiltIn.argtypes = [c_int, c_void_p, - c_void_p] - libmagick.AcquireKernelBuiltIn.restype = c_void_p - libmagick.AcquireKernelInfo.argtypes = [c_char_p, c_void_p] - libmagick.AcquireKernelInfo.restype = c_void_p - libmagick.DestroyKernelInfo.argtypes = [c_void_p] - libmagick.DestroyKernelInfo.restype = c_void_p diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/drawing_wand.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/drawing_wand.py deleted file mode 100644 index a0aba75..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/drawing_wand.py +++ /dev/null @@ -1,277 +0,0 @@ -""":mod:`wand.cdefs.drawing_wand` --- Drawing-Wand definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import (POINTER, c_void_p, c_char_p, c_double, c_int, c_uint, - c_size_t, c_ubyte, c_ulong) -from wand.cdefs.wandtypes import c_ssize_t -from wand.cdefs.structures import PointInfo - -__all__ = ('load',) - - -def load(lib, IM_VERSION): - """Define Drawing Wand methods. The ImageMagick version is given as a - second argument for comparison. This will quick to determine which methods - are available from the library, and can be implemented as:: - - if IM_VERSION < 0x700: - # ... do ImageMagick-6 methods ... - else - # ... do ImageMagick-7 methods ... - - .. seealso:: - - #include "wand/drawing-wand.h" - // Or - #include "MagickWand/drawing-wand.h" - - :param lib: the loaded ``MagickWand`` library - :type lib: :class:`ctypes.CDLL` - :param IM_VERSION: the ImageMagick version number (i.e. 0x0689) - :type IM_VERSION: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - - """ - is_im_6 = IM_VERSION < 0x700 - is_im_7 = IM_VERSION >= 0x700 - lib.NewDrawingWand.restype = c_void_p - lib.CloneDrawingWand.argtypes = [c_void_p] - lib.CloneDrawingWand.restype = c_void_p - lib.DestroyDrawingWand.argtypes = [c_void_p] - lib.DestroyDrawingWand.restype = c_void_p - lib.IsDrawingWand.argtypes = [c_void_p] - lib.IsDrawingWand.restype = c_int - lib.DrawGetException.argtypes = [c_void_p, POINTER(c_int)] - lib.DrawGetException.restype = c_void_p - lib.DrawClearException.argtypes = [c_void_p] - lib.DrawClearException.restype = c_int - lib.DrawAffine.argtypes = [c_void_p, c_void_p] - if is_im_7: - lib.DrawAlpha.argtypes = [c_void_p, c_double, c_double, c_int] - lib.DrawComment.argtypes = [c_void_p, c_char_p] - lib.DrawComposite.argtypes = [ - c_void_p, c_int, c_double, c_double, c_double, c_double, c_void_p - ] - lib.DrawComposite.restype = c_uint - lib.DrawSetBorderColor.argtypes = [c_void_p, c_void_p] - lib.DrawSetClipPath.argtypes = [c_void_p, c_char_p] - lib.DrawSetClipPath.restype = c_int - lib.DrawSetClipRule.argtypes = [c_void_p, c_uint] - lib.DrawSetClipUnits.argtypes = [c_void_p, c_uint] - lib.DrawSetFont.argtypes = [c_void_p, c_char_p] - lib.DrawSetFontFamily.argtypes = [c_void_p, c_char_p] - lib.DrawSetFontFamily.restype = c_uint - lib.DrawSetFontResolution.argtypes = [c_void_p, c_double, c_double] - lib.DrawSetFontResolution.restype = c_uint - lib.DrawSetFontSize.argtypes = [c_void_p, c_double] - lib.DrawSetFontStretch.argtypes = [c_void_p, c_int] - lib.DrawSetFontStyle.argtypes = [c_void_p, c_int] - lib.DrawSetFontWeight.argtypes = [c_void_p, c_size_t] - lib.DrawSetFillColor.argtypes = [c_void_p, c_void_p] - lib.DrawSetFillOpacity.argtypes = [c_void_p, c_double] - lib.DrawSetFillPatternURL.argtypes = [c_void_p, c_char_p] - lib.DrawSetFillPatternURL.restype = c_uint - lib.DrawSetFillRule.argtypes = [c_void_p, c_uint] - lib.DrawSetOpacity.argtypes = [c_void_p, c_double] - lib.DrawSetStrokeAntialias.argtypes = [c_void_p, c_int] - lib.DrawSetStrokeColor.argtypes = [c_void_p, c_void_p] - lib.DrawSetStrokeDashArray.argtypes = [ - c_void_p, c_size_t, POINTER(c_double) - ] - lib.DrawSetStrokeDashOffset.argtypes = [c_void_p, c_double] - lib.DrawSetStrokeLineCap.argtypes = [c_void_p, c_int] - lib.DrawSetStrokeLineJoin.argtypes = [c_void_p, c_int] - lib.DrawSetStrokeMiterLimit.argtypes = [c_void_p, c_size_t] - lib.DrawSetStrokeOpacity.argtypes = [c_void_p, c_double] - lib.DrawSetStrokePatternURL.argtypes = [c_void_p, c_char_p] - lib.DrawSetStrokePatternURL.restype = c_uint - lib.DrawSetStrokeWidth.argtypes = [c_void_p, c_double] - lib.DrawSetTextAlignment.argtypes = [c_void_p, c_int] - lib.DrawSetTextAntialias.argtypes = [c_void_p, c_int] - lib.DrawSetTextDecoration.argtypes = [c_void_p, c_int] - try: - lib.DrawSetTextDirection.argtypes = [c_void_p, c_int] - except AttributeError: - lib.DrawSetTextDirection = None - lib.DrawSetTextEncoding.argtypes = [c_void_p, c_char_p] - try: - lib.DrawSetTextInterlineSpacing.argtypes = [c_void_p, c_double] - except AttributeError: - lib.DrawSetTextInterlineSpacing = None - lib.DrawSetTextInterwordSpacing.argtypes = [c_void_p, c_double] - lib.DrawSetTextKerning.argtypes = [c_void_p, c_double] - lib.DrawSetTextUnderColor.argtypes = [c_void_p, c_void_p] - lib.DrawSetVectorGraphics.argtypes = [c_void_p, c_char_p] - lib.DrawSetVectorGraphics.restype = c_int - lib.DrawResetVectorGraphics.argtypes = [c_void_p] - lib.DrawSetViewbox.argtypes = [ - c_void_p, c_ssize_t, c_ssize_t, c_ssize_t, c_ssize_t - ] - lib.DrawGetBorderColor.argtypes = [c_void_p, c_void_p] - lib.DrawGetClipPath.argtypes = [c_void_p] - lib.DrawGetClipPath.restype = c_void_p - lib.DrawGetClipRule.argtypes = [c_void_p] - lib.DrawGetClipRule.restype = c_uint - lib.DrawGetClipUnits.argtypes = [c_void_p] - lib.DrawGetClipUnits.restype = c_uint - lib.DrawGetFillColor.argtypes = [c_void_p, c_void_p] - lib.DrawGetFillOpacity.argtypes = [c_void_p] - lib.DrawGetFillOpacity.restype = c_double - lib.DrawGetFillRule.argtypes = [c_void_p] - lib.DrawGetFillRule.restype = c_uint - lib.DrawGetOpacity.argtypes = [c_void_p] - lib.DrawGetOpacity.restype = c_double - lib.DrawGetStrokeAntialias.argtypes = [c_void_p] - lib.DrawGetStrokeAntialias.restype = c_int - lib.DrawGetStrokeColor.argtypes = [c_void_p, c_void_p] - lib.DrawGetStrokeDashArray.argtypes = [c_void_p, POINTER(c_size_t)] - lib.DrawGetStrokeDashArray.restype = POINTER(c_double) - lib.DrawGetStrokeDashOffset.argtypes = [c_void_p] - lib.DrawGetStrokeDashOffset.restype = c_double - lib.DrawGetStrokeLineCap.argtypes = [c_void_p] - lib.DrawGetStrokeLineCap.restype = c_int - lib.DrawGetStrokeLineJoin.argtypes = [c_void_p] - lib.DrawGetStrokeLineJoin.restype = c_int - lib.DrawGetStrokeMiterLimit.argtypes = [c_void_p] - lib.DrawGetStrokeMiterLimit.restype = c_size_t - lib.DrawGetStrokeOpacity.argtypes = [c_void_p] - lib.DrawGetStrokeOpacity.restype = c_double - lib.DrawGetStrokeWidth.argtypes = [c_void_p] - lib.DrawGetStrokeWidth.restype = c_double - lib.DrawGetFont.argtypes = [c_void_p] - lib.DrawGetFont.restype = c_void_p - lib.DrawGetFontFamily.argtypes = [c_void_p] - lib.DrawGetFontFamily.restype = c_void_p - lib.DrawGetFontResolution.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - lib.DrawGetFontResolution.restype = c_uint - lib.DrawGetFontSize.argtypes = [c_void_p] - lib.DrawGetFontSize.restype = c_double - lib.DrawGetFontStyle.argtypes = [c_void_p] - lib.DrawGetFontStyle.restype = c_int - lib.DrawGetFontWeight.argtypes = [c_void_p] - lib.DrawGetFontWeight.restype = c_size_t - lib.DrawGetFontStretch.argtypes = [c_void_p] - lib.DrawGetFontStretch.restype = c_int - lib.DrawGetTextAlignment.argtypes = [c_void_p] - lib.DrawGetTextAlignment.restype = c_int - lib.DrawGetTextAntialias.argtypes = [c_void_p] - lib.DrawGetTextAntialias.restype = c_int - lib.DrawGetTextDecoration.argtypes = [c_void_p] - lib.DrawGetTextDecoration.restype = c_int - try: - lib.DrawGetTextDirection.argtypes = [c_void_p] - lib.DrawGetTextDirection.restype = c_int - except AttributeError: - lib.DrawGetTextDirection = None - lib.DrawGetTextEncoding.argtypes = [c_void_p] - lib.DrawGetTextEncoding.restype = c_void_p - try: - lib.DrawGetTextInterlineSpacing.argtypes = [c_void_p] - lib.DrawGetTextInterlineSpacing.restype = c_double - except AttributeError: - lib.DrawGetTextInterlineSpacing = None - lib.DrawGetTextInterwordSpacing.argtypes = [c_void_p] - lib.DrawGetTextInterwordSpacing.restype = c_double - lib.DrawGetTextKerning.argtypes = [c_void_p] - lib.DrawGetTextKerning.restype = c_double - lib.DrawGetTextUnderColor.argtypes = [c_void_p, c_void_p] - lib.DrawGetVectorGraphics.argtypes = [c_void_p] - lib.DrawGetVectorGraphics.restype = c_void_p - lib.DrawSetGravity.argtypes = [c_void_p, c_int] - lib.DrawGetGravity.argtypes = [c_void_p] - lib.DrawGetGravity.restype = c_int - lib.ClearDrawingWand.argtypes = [c_void_p] - lib.DrawAnnotation.argtypes = [ - c_void_p, c_double, c_double, POINTER(c_ubyte) - ] - lib.DrawArc.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double, c_double, c_double - ] - lib.DrawBezier.argtypes = [c_void_p, c_ulong, POINTER(PointInfo)] - lib.DrawCircle.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.DrawColor.argtypes = [c_void_p, c_double, c_double, c_uint] - lib.DrawEllipse.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double, c_double, c_double - ] - lib.DrawLine.argtypes = [c_void_p, c_double, c_double, c_double, c_double] - if is_im_6: - lib.DrawMatte.argtypes = [c_void_p, c_double, c_double, c_int] - else: - lib.DrawMatte = None - lib.DrawPathClose.argtypes = [c_void_p] - lib.DrawPathCurveToAbsolute.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double, c_double, c_double - ] - lib.DrawPathCurveToRelative.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double, c_double, c_double - ] - lib.DrawPathCurveToQuadraticBezierAbsolute.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.DrawPathCurveToQuadraticBezierRelative.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.DrawPathCurveToQuadraticBezierSmoothAbsolute.argtypes = [ - c_void_p, c_double, c_double - ] - lib.DrawPathCurveToQuadraticBezierSmoothRelative.argtypes = [ - c_void_p, c_double, c_double - ] - lib.DrawPathCurveToSmoothAbsolute.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.DrawPathCurveToSmoothRelative.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.DrawPathEllipticArcAbsolute.argtypes = [ - c_void_p, c_double, c_double, c_double, c_uint, c_uint, c_double, - c_double - ] - lib.DrawPathEllipticArcRelative.argtypes = [ - c_void_p, c_double, c_double, c_double, c_uint, c_uint, c_double, - c_double - ] - lib.DrawPathFinish.argtypes = [c_void_p] - lib.DrawPathLineToAbsolute.argtypes = [c_void_p, c_double, c_double] - lib.DrawPathLineToRelative.argtypes = [c_void_p, c_double, c_double] - lib.DrawPathLineToHorizontalAbsolute.argtypes = [c_void_p, c_double] - lib.DrawPathLineToHorizontalRelative.argtypes = [c_void_p, c_double] - lib.DrawPathLineToVerticalAbsolute.argtypes = [c_void_p, c_double] - lib.DrawPathLineToVerticalRelative.argtypes = [c_void_p, c_double] - lib.DrawPathMoveToAbsolute.argtypes = [c_void_p, c_double, c_double] - lib.DrawPathMoveToRelative.argtypes = [c_void_p, c_double, c_double] - lib.DrawPathStart.argtypes = [c_void_p] - lib.DrawPoint.argtypes = [c_void_p, c_double, c_double] - lib.DrawPolygon.argtypes = [c_void_p, c_ulong, POINTER(PointInfo)] - lib.DrawPolyline.argtypes = [c_void_p, c_ulong, POINTER(PointInfo)] - lib.DrawRotate.argtypes = [c_void_p, c_double] - lib.DrawRectangle.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.DrawRoundRectangle.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double, c_double, c_double - ] - lib.DrawScale.argtypes = [c_void_p, c_double, c_double] - lib.DrawSkewX.argtypes = [c_void_p, c_double] - lib.DrawSkewY.argtypes = [c_void_p, c_double] - lib.DrawTranslate.argtypes = [c_void_p, c_double, c_double] - lib.PushDrawingWand.argtypes = [c_void_p] - lib.PushDrawingWand.restype = c_uint - lib.DrawPushClipPath.argtypes = [c_void_p, c_char_p] - lib.DrawPushDefs.argtypes = [c_void_p] - lib.DrawPushPattern.argtypes = [ - c_void_p, c_char_p, c_double, c_double, c_double, c_double - ] - lib.DrawPushClipPath.restype = c_uint - lib.PopDrawingWand.argtypes = [c_void_p] - lib.PopDrawingWand.restype = c_uint - lib.DrawPopClipPath.argtypes = [c_void_p] - lib.DrawPopDefs.argtypes = [c_void_p] - lib.DrawPopPattern.argtypes = [c_void_p] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_image.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_image.py deleted file mode 100644 index 6971f3b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_image.py +++ /dev/null @@ -1,1247 +0,0 @@ -""":mod:`wand.cdefs.magick_image` --- Magick-Image definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import (CFUNCTYPE, POINTER, c_void_p, c_int, c_size_t, c_double, - c_char_p, c_ubyte, c_bool) -from wand.cdefs.wandtypes import c_ssize_t - -__all__ = ('MagickProgressMonitor', 'load') - - -#: (:class:`ctypes.CFUNCTYPE`) a function type to allow ImageMagick's progress -#: monitoring to call a python function. For example:: -#: -#: def myCallBack(filename, offset, size, user_data): -#: print(filename, offset, '/', size) -#: return True -#: iMyCallBack = MagickProgressMonitor(myCallBack) -#: library.MagickSetImageProgressMonitor(wand_instance, -#: iMyCallBack, -#: None) -#: -#: .. note:: -#: -#: TODO - Move to isolated module. This shouldn't be defined at time of -#: mload. It might be wiser to create a method to allow the user to ask for -#: C-function-type. -MagickProgressMonitor = CFUNCTYPE(c_bool, - c_char_p, - c_ssize_t, - c_size_t, - c_void_p) - - -def load(lib, IM_VERSION): - """Define Magick Image methods. The ImageMagick version is given as a - second argument for comparison. This will quick to determine which methods - are available from the library, and can be implemented as:: - - if IM_VERSION < 0x700: - # ... do ImageMagick-6 methods ... - else - # ... do ImageMagick-7 methods ... - - .. seealso:: - - #include "wand/magick-image.h" - // Or - #include "MagickWand/magick-image.h" - - :param lib: the loaded ``MagickWand`` library - :type lib: :class:`ctypes.CDLL` - :param IM_VERSION: the ImageMagick version number (i.e. 0x0689) - :type IM_VERSION: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - - """ - is_im_6 = IM_VERSION < 0x700 - is_im_7 = IM_VERSION >= 0x700 - lib.GetImageFromMagickWand.argtypes = [c_void_p] - lib.GetImageFromMagickWand.restype = c_void_p - lib.MagickAdaptiveBlurImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickAdaptiveBlurImage.restype = c_bool - if is_im_6: - lib.MagickAdaptiveBlurImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - lib.MagickAdaptiveBlurImageChannel.restype = c_bool - lib.MagickAdaptiveResizeImage.argtypes = [c_void_p, c_size_t, c_size_t] - lib.MagickAdaptiveResizeImage.restype = c_bool - lib.MagickAdaptiveSharpenImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickAdaptiveSharpenImage.restype = c_bool - if is_im_6: - lib.MagickAdaptiveSharpenImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - lib.MagickAdaptiveSharpenImageChannel.restype = c_bool - if is_im_6: - lib.MagickAdaptiveThresholdImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t - ] - lib.MagickAdaptiveThresholdImage.restype = c_bool - else: - lib.MagickAdaptiveThresholdImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_double - ] - lib.MagickAdaptiveThresholdImage.restype = c_bool - lib.MagickAddImage.argtypes = [c_void_p, c_void_p] - lib.MagickAddImage.restype = c_bool - if is_im_6: - lib.MagickAddNoiseImage.argtypes = [c_void_p, c_int] - lib.MagickAddNoiseImage.restype = c_bool - else: - lib.MagickAddNoiseImage.argtypes = [c_void_p, c_int, c_double] - lib.MagickAddNoiseImage.restype = c_bool - if is_im_6: - lib.MagickAddNoiseImageChannel.argtypes = [c_void_p, c_int, c_int] - lib.MagickAddNoiseImageChannel.restype = c_bool - lib.MagickAffineTransformImage.argtypes = [c_void_p, c_void_p] - lib.MagickAffineTransformImage.restype = c_bool - lib.MagickAnnotateImage.argtypes = [ - c_void_p, c_void_p, c_double, c_double, c_double, c_char_p - ] - lib.MagickAnnotateImage.restype = c_int - lib.MagickAnimateImages.argtypes = [c_void_p, c_char_p] - lib.MagickAnimateImages.restype = c_bool - lib.MagickAppendImages.argtypes = [c_void_p, c_int] - lib.MagickAppendImages.restype = c_void_p - lib.MagickAutoGammaImage.argtypes = [c_void_p] - lib.MagickAutoGammaImage.restype = c_bool - if is_im_6: - lib.MagickAutoGammaImageChannel.argtypes = [c_void_p, c_int] - lib.MagickAutoGammaImageChannel.restype = c_bool - lib.MagickAutoLevelImage.argtypes = [c_void_p] - lib.MagickAutoLevelImage.restype = c_bool - if is_im_6: - lib.MagickAutoLevelImageChannel.argtypes = [c_void_p, c_int] - lib.MagickAutoLevelImageChannel.restype = c_bool - try: - lib.MagickAutoOrientImage.argtypes = [c_void_p] - except AttributeError: - # MagickAutoOrientImage was added in 6.8.9+, we have a fallback - # function, so we pass silently if we cannot import it. - pass - if IM_VERSION >= 0x708: - try: - lib.MagickAutoThresholdImage.argtypes = [c_void_p, c_int] - lib.MagickAutoThresholdImage.restype = c_bool - except AttributeError: - lib.MagickAutoThresholdImage = None - else: - lib.MagickAutoThresholdImage = None - lib.MagickBlackThresholdImage.argtypes = [c_void_p, c_void_p] - lib.MagickBlackThresholdImage.restype = c_bool - lib.MagickBlueShiftImage.argtypes = [c_void_p, c_double] - lib.MagickBlueShiftImage.restype = c_bool - lib.MagickBlurImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickBlurImage.restype = c_bool - if is_im_6: - lib.MagickBlurImageChannel.argtypes = [c_void_p, c_int, c_double, - c_double] - lib.MagickBlurImageChannel.restype = c_bool - border_image_args = [c_void_p, c_void_p, c_size_t, c_size_t] - if is_im_7: - border_image_args.append(c_int) - lib.MagickBorderImage.argtypes = border_image_args - lib.MagickBorderImage.restype = c_bool - lib.MagickBrightnessContrastImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickBrightnessContrastImage.restype = c_bool - if is_im_6: - lib.MagickBrightnessContrastImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - lib.MagickBrightnessContrastImageChannel.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickCannyEdgeImage.argtypes = [c_void_p, c_double, c_double, - c_double, c_double] - lib.MagickCannyEdgeImage.restype = c_bool - except AttributeError: - lib.MagickCannyEdgeImage = None - else: - lib.MagickCannyEdgeImage = None - lib.MagickCharcoalImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickCharcoalImage.restype = c_bool - lib.MagickChopImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickChopImage.restype = c_bool - if is_im_7: - try: - lib.MagickCLAHEImage.argtypes = [c_void_p, c_size_t, c_size_t, - c_double, c_double] - lib.MagickCLAHEImage.restype = c_bool - except AttributeError: - lib.MagickCLAHEImage = None - else: - lib.MagickCLAHEImage = None - lib.MagickClampImage.argtypes = [c_void_p] - lib.MagickClampImage.restype = c_bool - if is_im_6: - lib.MagickClampImageChannel.argtypes = [c_void_p, c_int] - lib.MagickClampImageChannel.restype = c_bool - lib.MagickClipImage.argtypes = [c_void_p] - lib.MagickClipImage.restype = c_bool - lib.MagickClipImagePath.argtypes = [c_void_p, c_char_p, c_bool] - lib.MagickClipImagePath.restype = c_bool - if is_im_7: - lib.MagickClutImage.argtypes = [c_void_p, c_void_p, c_int] - lib.MagickClutImage.restype = c_bool - else: - lib.MagickClutImage.argtypes = [c_void_p, c_void_p] - lib.MagickClutImage.restype = c_bool - if is_im_6: - lib.MagickClutImageChannel.argtypes = [c_void_p, c_int, c_void_p] - lib.MagickClutImageChannel.restype = c_bool - lib.MagickCoalesceImages.argtypes = [c_void_p] - lib.MagickCoalesceImages.restype = c_void_p - lib.MagickColorDecisionListImage.argtypes = [c_void_p, c_char_p] - lib.MagickColorDecisionListImage.restype = c_bool - lib.MagickColorizeImage.argtypes = [c_void_p, c_void_p, c_void_p] - lib.MagickColorizeImage.restype = c_bool - lib.MagickColorMatrixImage.argtypes = [c_void_p, c_void_p] - lib.MagickColorMatrixImage.restype = c_bool - if IM_VERSION > 0x709: - lib.MagickColorThresholdImage.argtypes = [c_void_p, c_void_p, c_void_p] - lib.MagickColorThresholdImage.restype = c_bool - else: - lib.MagickColorThresholdImage = None - lib.MagickCommentImage.argtypes = [c_void_p, c_char_p] - lib.MagickCommentImage.restype = c_bool - lib.MagickCombineImages.argtypes = [c_void_p, c_int] - lib.MagickCombineImages.restype = c_void_p - if is_im_6: - lib.MagickCompareImageChannels.argtypes = [ - c_void_p, c_void_p, c_int, c_double - ] - lib.MagickCompareImageChannels.restype = c_void_p - lib.MagickCompareImageLayers.argtypes = [c_void_p, c_int] - lib.MagickCompareImageLayers.restype = c_void_p - lib.MagickCompareImages.argtypes = [ - c_void_p, c_void_p, c_int, POINTER(c_double) - ] - lib.MagickCompareImages.restype = c_void_p - if is_im_6: - try: - lib.MagickCompareImageLayers.argtypes = [c_void_p, c_int] - lib.MagickCompareImageLayers.restype = c_void_p - except AttributeError: - lib.MagickCompareImageLayers = None - else: - try: - lib.MagickCompareImagesLayers.argtypes = [c_void_p, c_int] - lib.MagickCompareImagesLayers.restype = c_void_p - except AttributeError: - lib.MagickCompareImagesLayers = None - if IM_VERSION >= 0x708: - try: - lib.MagickComplexImages.argtypes = [c_void_p, c_int] - lib.MagickComplexImages.restype = c_void_p - except AttributeError: - lib.MagickComplexImages = None - else: - lib.MagickComplexImages = None - if is_im_6: - lib.MagickCompositeImage.argtypes = [ - c_void_p, c_void_p, c_int, c_ssize_t, c_ssize_t - ] - else: - lib.MagickCompositeImage.argtypes = [ - c_void_p, c_void_p, c_int, c_bool, c_ssize_t, c_ssize_t - ] - lib.MagickCompositeImage.restype = c_bool - try: - lib.MagickCompositeLayers.argtypes = [ - c_void_p, c_void_p, c_int, c_ssize_t, c_ssize_t - ] - lib.MagickCompositeLayers.restype = c_bool - except AttributeError: - lib.MagickCompositeLayers = None - if is_im_6: - lib.MagickCompositeImageChannel.argtypes = [ - c_void_p, c_int, c_void_p, c_int, c_ssize_t, c_ssize_t - ] - lib.MagickCompositeImageChannel.restype = c_bool - else: - lib.MagickCompositeImageChannel = None - if IM_VERSION >= 0x708: - try: - lib.MagickConnectedComponentsImage.argtypes = [ - c_void_p, c_size_t, POINTER(c_void_p) - ] - lib.MagickConnectedComponentsImage.restype = c_bool - except AttributeError: - lib.MagickConnectedComponentsImage = None - else: - lib.MagickConnectedComponentsImage = None - lib.MagickConstituteImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_char_p, c_int, c_void_p - ] - lib.MagickContrastImage.argtypes = [c_void_p, c_bool] - lib.MagickContrastImage.restype = c_bool - lib.MagickContrastStretchImage.argtypes = [c_void_p, c_double, c_double] - if is_im_6: - lib.MagickContrastStretchImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - else: - lib.MagickContrastStretchImageChannel = None - lib.MagickConvolveImage.argtypes = [c_void_p, c_size_t, c_double] - lib.MagickConvolveImage.restype = c_bool - if is_im_6: - lib.MagickConvolveImageChannel.argtypes = [ - c_void_p, c_int, c_size_t, c_double - ] - lib.MagickConvolveImageChannel.restype = c_bool - lib.MagickCropImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickCropImage.restype = c_bool - lib.MagickCycleColormapImage.argtypes = [c_void_p, c_ssize_t] - lib.MagickCycleColormapImage.restype = c_bool - lib.MagickDecipherImage.argtypes = [c_void_p, c_char_p] - lib.MagickDecipherImage.restype = c_bool - lib.MagickDeconstructImages.argtypes = [c_void_p] - lib.MagickDeconstructImages.restype = c_void_p - lib.MagickDeskewImage.argtypes = [c_void_p, c_double] - lib.MagickDeskewImage.restype = c_bool - lib.MagickDespeckleImage.argtypes = [c_void_p] - lib.MagickDespeckleImage.restype = c_bool - lib.MagickDestroyImage.argtypes = [c_void_p] - lib.MagickDestroyImage.restype = c_void_p - lib.MagickDisplayImage.argtypes = [c_void_p, c_char_p] - lib.MagickDisplayImage.restype = c_bool - lib.MagickDisplayImages.argtypes = [c_void_p, c_char_p] - lib.MagickDisplayImages.restype = c_bool - lib.MagickDistortImage.argtypes = [ - c_void_p, c_int, c_size_t, POINTER(c_double), c_int - ] - lib.MagickDistortImage.restype = c_int - lib.MagickDrawImage.argtypes = [c_void_p, c_void_p] - lib.MagickDrawImage.restype = c_int - lib.MagickEdgeImage.argtypes = [c_void_p, c_double] - lib.MagickEdgeImage.restype = c_bool - lib.MagickEmbossImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickEmbossImage.restype = c_bool - lib.MagickEncipherImage.argtypes = [c_void_p, c_char_p] - lib.MagickEncipherImage.restype = c_bool - lib.MagickEnhanceImage.argtypes = [c_void_p] - lib.MagickEnhanceImage.restype = c_bool - lib.MagickEqualizeImage.argtypes = [c_void_p] - lib.MagickEqualizeImage.restype = c_bool - if is_im_6: - lib.MagickEqualizeImageChannel.argtypes = [c_void_p, c_int] - lib.MagickEqualizeImageChannel.restype = c_bool - lib.MagickEvaluateImage.argtypes = [c_void_p, c_int, c_double] - if is_im_6: - lib.MagickEvaluateImageChannel.argtypes = [ - c_void_p, c_int, c_int, c_double - ] - else: - lib.MagickEvaluateImageChannel = None - lib.MagickEvaluateImages.argtypes = [c_void_p, c_int] - lib.MagickEvaluateImages.restype = c_void_p - lib.MagickExportImagePixels.argtypes = [ - c_void_p, c_ssize_t, c_ssize_t, c_size_t, c_size_t, c_char_p, c_int, - c_void_p - ] - lib.MagickExportImagePixels.restype = c_bool - lib.MagickExtentImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickExtentImage.restype = c_bool - if is_im_6: - lib.MagickFilterImage.argtypes = [c_void_p, c_void_p] - lib.MagickFilterImage.restype = c_bool - lib.MagickFilterImageChannel.argtypes = [c_void_p, c_int, c_void_p] - lib.MagickFilterImageChannel.restype = c_bool - lib.MagickFlipImage.argtypes = [c_void_p] - lib.MagickFlipImage.restype = c_bool - lib.MagickFloodfillPaintImage.argtypes = [ - c_void_p, c_int, c_void_p, c_double, c_void_p, c_ssize_t, c_ssize_t, - c_bool - ] - lib.MagickFloodfillPaintImage.restype = c_bool - lib.MagickFlopImage.argtypes = [c_void_p] - lib.MagickFlopImage.restype = c_bool - lib.MagickForwardFourierTransformImage.argtypes = [c_void_p, c_bool] - lib.MagickForwardFourierTransformImage.restype = c_bool - if is_im_6: - lib.MagickFrameImage.argtypes = [ - c_void_p, c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - else: - lib.MagickFrameImage.argtypes = [ - c_void_p, c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t, - c_int - ] - lib.MagickFrameImage.restype = c_bool - lib.MagickFunctionImage.argtypes = [ - c_void_p, c_int, c_size_t, POINTER(c_double) - ] - lib.MagickFunctionImage.restype = c_bool - if is_im_6: - lib.MagickFunctionImageChannel.argtypes = [ - c_void_p, c_int, c_int, c_size_t, POINTER(c_double) - ] - lib.MagickFunctionImageChannel.restype = c_bool - else: - lib.MagickFunctionImageChannel = None - lib.MagickFxImage.argtypes = [c_void_p, c_char_p] - lib.MagickFxImage.restype = c_void_p - if is_im_6: - lib.MagickFxImageChannel.argtypes = [c_void_p, c_int, c_char_p] - lib.MagickFxImageChannel.restype = c_void_p - else: - lib.MagickFxImageChannel = None - lib.MagickGammaImage.argtypes = [c_void_p, c_double] - lib.MagickGammaImage.restype = c_bool - if is_im_6: - lib.MagickGammaImageChannel.argtypes = [c_void_p, c_int, c_double] - lib.MagickGammaImageChannel.restype = c_bool - else: - lib.MagickGammaImageChannel = None - lib.MagickGaussianBlurImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickGaussianBlurImage.restype = c_bool - if is_im_6: - lib.MagickGaussianBlurImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - lib.MagickGaussianBlurImageChannel.restype = c_bool - else: - lib.MagickGaussianBlurImageChannel = None - lib.MagickGetImage.argtypes = [c_void_p] - lib.MagickGetImage.restype = c_void_p - lib.MagickGetImageAlphaChannel.argtypes = [c_void_p] - lib.MagickGetImageAlphaChannel.restype = c_bool - lib.MagickGetImageBackgroundColor.argtypes = [c_void_p, c_void_p] - lib.MagickGetImageBackgroundColor.restype = c_bool - lib.MagickGetImageBlob.argtypes = [c_void_p, POINTER(c_size_t)] - lib.MagickGetImageBlob.restype = POINTER(c_ubyte) - if is_im_6: - lib.MagickGetImageBluePrimary.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - else: - lib.MagickGetImageBluePrimary.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageBluePrimary.restype = c_bool - lib.MagickGetImageBorderColor.argtypes = [c_void_p, c_void_p] - lib.MagickGetImageBorderColor.restype = c_bool - if is_im_6: - lib.MagickGetImageChannelDepth.argtypes = [c_void_p, c_int] - lib.MagickGetImageChannelDepth.restype = c_size_t - lib.MagickGetImageChannelFeatures.argtypes = [c_void_p, c_size_t] - lib.MagickGetImageChannelFeatures.restype = c_void_p - lib.MagickGetImageChannelKurtosis.argtypes = [ - c_void_p, c_int, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageChannelKurtosis.restype = c_bool - lib.MagickGetImageChannelMean.argtypes = [ - c_void_p, c_int, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageChannelMean.restype = c_bool - lib.MagickGetImageChannelRange.argtypes = [ - c_void_p, c_int, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageChannelRange.restype = c_bool - lib.MagickGetImageChannelStatistics.argtypes = [c_void_p] - lib.MagickGetImageChannelStatistics.restype = c_void_p - lib.MagickGetImageClipMask.argtypes = [c_void_p] - lib.MagickGetImageClipMask.restype = c_void_p - else: - lib.MagickGetImageChannelDepth = None - lib.MagickGetImageColormapColor.argtypes = [c_void_p, c_size_t, c_void_p] - lib.MagickGetImageColormapColor.restype = c_bool - lib.MagickGetImageColors.argtypes = [c_void_p] - lib.MagickGetImageColors.restype = c_size_t - lib.MagickGetImageColorspace.argtypes = [c_void_p] - lib.MagickGetImageColorspace.restype = c_int - lib.MagickGetImageCompose.argtypes = [c_void_p] - lib.MagickGetImageCompose.restype = c_int - lib.MagickGetImageCompression.argtypes = [c_void_p] - lib.MagickGetImageCompression.restype = c_int - lib.MagickGetImageCompressionQuality.argtypes = [c_void_p] - lib.MagickGetImageCompressionQuality.restype = c_ssize_t - try: - lib.MagickGetImageEndian.argtypes = [c_void_p] - lib.MagickGetImageEndian.restype = c_int - except AttributeError: - lib.MagickGetImageEndian = None - lib.MagickGetImageDelay.argtypes = [c_void_p] - lib.MagickGetImageDelay.restype = c_size_t - lib.MagickGetImageDepth.argtypes = [c_void_p] - lib.MagickGetImageDepth.restype = c_size_t - lib.MagickGetImageDispose.argtypes = [c_void_p] - lib.MagickGetImageDispose.restype = c_int - lib.MagickGetImageDistortion.argtypes = [ - c_void_p, c_void_p, c_int, POINTER(c_double) - ] - lib.MagickGetImageDistortion.restype = c_bool - if is_im_7: - lib.MagickGetImageFeatures.argtypes = [c_void_p, c_size_t] - lib.MagickGetImageFeatures.restype = c_void_p - lib.MagickGetImageFilename.argtypes = [c_void_p] - lib.MagickGetImageFilename.restype = c_void_p - if IM_VERSION >= 0x710: - try: - lib.MagickGetImageFilter.argtypes = [c_void_p] - lib.MagickGetImageFilter.restype = c_int - except AttributeError: - lib.MagickGetImageFilter = None - else: - lib.MagickGetImageFilter = None - lib.MagickGetImageFormat.argtypes = [c_void_p] - lib.MagickGetImageFormat.restype = c_void_p - lib.MagickGetImageFuzz.argtypes = [c_void_p] - lib.MagickGetImageFuzz.restype = c_double - lib.MagickGetImageGamma.argtypes = [c_void_p] - lib.MagickGetImageGamma.restype = c_double - lib.MagickGetImageGravity.argtypes = [c_void_p] - lib.MagickGetImageGravity.restype = c_int - if is_im_6: - lib.MagickGetImageGreenPrimary.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - else: - lib.MagickGetImageGreenPrimary.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageGreenPrimary.restype = c_bool - lib.MagickGetImageHeight.argtypes = [c_void_p] - lib.MagickGetImageHeight.restype = c_size_t - lib.MagickGetImageHistogram.argtypes = [c_void_p, POINTER(c_size_t)] - lib.MagickGetImageHistogram.restype = POINTER(c_void_p) - lib.MagickGetImageInterlaceScheme.argtypes = [c_void_p] - lib.MagickGetImageInterlaceScheme.restype = c_int - lib.MagickGetImageIterations.argtypes = [c_void_p] - lib.MagickGetImageIterations.restype = c_size_t - lib.MagickGetImageInterpolateMethod.argtypes = [c_void_p] - lib.MagickGetImageInterpolateMethod.restype = c_int - if is_im_7: - lib.MagickGetImageKurtosis.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageKurtosis.restype = c_bool - lib.MagickGetImageLength.argtypes = [c_void_p, POINTER(c_size_t)] - lib.MagickGetImageLength.restype = c_bool - if is_im_7: - lib.MagickGetImageMask.argtypes = [c_void_p, c_int] - lib.MagickGetImageMask.restype = c_void_p - else: - lib.MagickGetImageMask = None - lib.MagickGetImageMatteColor.argtypes = [c_void_p, c_void_p] - lib.MagickGetImageMatteColor.restype = c_bool - if is_im_7: - lib.MagickGetImageMean.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageMean.restype = c_bool - lib.MagickGetImageOrientation.argtypes = [c_void_p] - lib.MagickGetImageOrientation.restype = c_int - lib.MagickGetImagePage.argtypes = [ - c_void_p, POINTER(c_size_t), POINTER(c_size_t), POINTER(c_ssize_t), - POINTER(c_ssize_t) - ] - lib.MagickGetImagePage.restype = c_bool - lib.MagickGetImagePixelColor.argtypes = [ - c_void_p, c_ssize_t, c_ssize_t, c_void_p - ] - lib.MagickGetImagePixelColor.restype = c_bool - lib.MagickGetImageRange.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageRange.restype = c_bool - if is_im_6: - lib.MagickGetImageRedPrimary.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - else: - lib.MagickGetImageRedPrimary.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageRedPrimary.restype = c_bool - lib.MagickGetImageRegion.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickGetImageRegion.restype = c_void_p - lib.MagickGetImageResolution.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageRenderingIntent.argtypes = [c_void_p] - lib.MagickGetImageRenderingIntent.restype = c_int - lib.MagickGetImageResolution.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageResolution.restype = c_bool - lib.MagickGetImagesBlob.argtypes = [c_void_p, POINTER(c_size_t)] - lib.MagickGetImagesBlob.restype = POINTER(c_ubyte) - lib.MagickGetImageScene.argtypes = [c_void_p] - lib.MagickGetImageScene.restype = c_size_t - lib.MagickGetImageSignature.argtypes = [c_void_p] - lib.MagickGetImageSignature.restype = c_void_p - lib.MagickGetImageTicksPerSecond.argtypes = [c_void_p] - lib.MagickGetImageTicksPerSecond.restype = c_size_t - lib.MagickGetImageTotalInkDensity.argtypes = [c_void_p] - lib.MagickGetImageTotalInkDensity.restype = c_double - lib.MagickGetImageType.argtypes = [c_void_p] - lib.MagickGetImageType.restype = c_int - lib.MagickGetImageUnits.argtypes = [c_void_p] - lib.MagickGetImageVirtualPixelMethod.argtypes = [c_void_p] - if is_im_6: - lib.MagickGetImageWhitePoint.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double) - ] - else: - lib.MagickGetImageWhitePoint.argtypes = [ - c_void_p, POINTER(c_double), POINTER(c_double), POINTER(c_double) - ] - lib.MagickGetImageWhitePoint.restype = c_bool - lib.MagickGetImageWidth.argtypes = [c_void_p] - lib.MagickGetImageWidth.restype = c_size_t - lib.MagickGetNumberImages.argtypes = [c_void_p] - lib.MagickGetNumberImages.restype = c_size_t - lib.MagickHaldClutImage.argtypes = [c_void_p, c_void_p] - lib.MagickHaldClutImage.restype = c_bool - if is_im_6: - lib.MagickHaldClutImageChannel.argtypes = [c_void_p, c_int, c_void_p] - lib.MagickHaldClutImageChannel.restype = c_bool - else: - lib.MagickHaldClutImageChannel = None - lib.MagickHasNextImage.argtypes = [c_void_p] - lib.MagickHasNextImage.restype = c_bool - lib.MagickHasPreviousImage.argtypes = [c_void_p] - lib.MagickHasPreviousImage.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickHoughLineImage.argtypes = [c_void_p, c_size_t, c_size_t, - c_size_t] - lib.MagickHoughLineImage.restype = c_bool - except AttributeError: - lib.MagickHoughLineImage = None - else: - lib.MagickHoughLineImage = None - lib.MagickIdentifyImage.argtypes = [c_void_p] - lib.MagickIdentifyImage.restype = c_void_p - if is_im_6: - lib.MagickImplodeImage.argtypes = [c_void_p, c_double] - else: - lib.MagickImplodeImage.argtypes = [c_void_p, c_double, c_int] - lib.MagickImplodeImage.restype = c_bool - lib.MagickImportImagePixels.argtypes = [ - c_void_p, c_ssize_t, c_ssize_t, c_size_t, c_size_t, c_char_p, c_int, - c_void_p - ] - lib.MagickImportImagePixels.restype = c_bool - lib.MagickInverseFourierTransformImage.argtypes = [ - c_void_p, c_void_p, c_double - ] - lib.MagickInverseFourierTransformImage.restype = c_bool - if IM_VERSION > 0x709: - try: - lib.MagickKmeansImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_double - ] - lib.MagickKmeansImage.restype = c_bool - except AttributeError: - lib.MagickKmeansImage = None - else: - lib.MagickKmeansImage = None - if IM_VERSION >= 0x708: - try: - lib.MagickKuwaharaImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickKuwaharaImage.restype = c_bool - except AttributeError: - lib.MagickKuwaharaImage = None - else: - lib.MagickKuwaharaImage = None - lib.MagickLabelImage.argtypes = [c_void_p, c_char_p] - lib.MagickLabelImage.restype = c_bool - lib.MagickLevelImage.argtypes = [c_void_p, c_double, c_double, c_double] - lib.MagickLevelImage.restype = c_bool - if is_im_6: - lib.MagickLevelImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double, c_double - ] - lib.MagickLevelImageChannel.restype = c_bool - else: - lib.MagickLevelImageChannel = None - if IM_VERSION >= 0x708: - try: - lib.MagickLevelImageColors.argtypes = [ - c_void_p, c_void_p, c_void_p, c_bool - ] - lib.MagickLevelImageColors.restype = c_bool - except AttributeError: - lib.MagickLevelImageColors = None - try: - lib.MagickLevelizeImage.argtypes = [c_void_p, c_double, c_double, - c_double] - lib.MagickLevelizeImage.restype = c_bool - except AttributeError: - lib.MagickLevelizeImage = None - else: - lib.MagickLevelImageColors = None - lib.MagickLevelizeImage = None - lib.MagickLinearStretchImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickLinearStretchImage.restype = c_bool - lib.MagickLiquidRescaleImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_double, c_double - ] - lib.MagickLiquidRescaleImage.restype = c_bool - try: - lib.MagickLocalContrastImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickLocalContrastImage.restype = c_bool - except AttributeError: - lib.MagickLocalContrastImage = None - lib.MagickMagnifyImage.argtypes = [c_void_p] - lib.MagickMagnifyImage.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickMeanShiftImage.argtypes = [c_void_p, c_size_t, c_size_t, - c_double] - lib.MagickMeanShiftImage.restype = c_bool - except AttributeError: - lib.MagickMeanShiftImage = None - else: - lib.MagickMeanShiftImage = None - if is_im_6: - lib.MagickMedianFilterImage.argtypes = [c_void_p, c_double] - lib.MagickMedianFilterImage.restype = c_bool - lib.MagickMergeImageLayers.argtypes = [c_void_p, c_int] - lib.MagickMergeImageLayers.restype = c_void_p - lib.MagickMinifyImage.argtypes = [c_void_p] - lib.MagickMinifyImage.restype = c_bool - try: - lib.MagickModeImage.argtypes = [c_void_p, c_double] - lib.MagickModeImage.restype = c_bool - except AttributeError: - pass - lib.MagickModulateImage.argtypes = [c_void_p, c_double, c_double, c_double] - lib.MagickModulateImage.restype = c_bool - lib.MagickMontageImage.argtypes = [ - c_void_p, c_void_p, c_char_p, c_char_p, c_int, c_char_p - ] - lib.MagickMontageImage.restype = c_void_p - lib.MagickMorphImages.argtypes = [c_void_p, c_size_t] - lib.MagickMorphImages.restype = c_void_p - lib.MagickMorphologyImage.argtypes = [c_void_p, c_int, c_ssize_t, c_void_p] - lib.MagickMorphologyImage.restype = c_bool - if is_im_6: - lib.MagickMorphologyImageChannel.argtypes = [ - c_void_p, c_int, c_int, c_ssize_t, c_void_p - ] - lib.MagickMorphologyImageChannel.restype = c_bool - else: - lib.MagickMorphologyImageChannel = None - lib.MagickMotionBlurImage.argtypes = [ - c_void_p, c_double, c_double, c_double - ] - lib.MagickMotionBlurImage.restype = c_bool - if is_im_6: - lib.MagickMotionBlurImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double, c_double - ] - lib.MagickMotionBlurImageChannel.restype = c_bool - else: - lib.MagickMotionBlurImageChannel = None - lib.MagickNegateImage.argtypes = [c_void_p, c_int] - lib.MagickNegateImage.restype = c_bool - if is_im_6: - lib.MagickNegateImageChannel.argtypes = [c_void_p, c_int, c_int] - lib.MagickNegateImageChannel.restype = c_bool - else: - lib.MagickNegateImageChannel = None - lib.MagickNewImage.argtypes = [c_void_p, c_int, c_int, c_void_p] - lib.MagickNewImage.restype = c_bool - lib.MagickNextImage.argtypes = [c_void_p] - lib.MagickNextImage.restype = c_bool - lib.MagickNormalizeImage.argtypes = [c_void_p] - lib.MagickNormalizeImage.restype = c_bool - if is_im_6: - lib.MagickNormalizeImageChannel.argtypes = [c_void_p, c_int] - lib.MagickNormalizeImageChannel.restype = c_bool - else: - lib.MagickNormalizeImageChannel = None - if is_im_6: - lib.MagickOilPaintImage.argtypes = [c_void_p, c_double] - lib.MagickOilPaintImage.restype = c_bool - else: - lib.MagickOilPaintImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickOilPaintImage.restype = c_bool - lib.MagickOpaquePaintImage.argtypes = [ - c_void_p, c_void_p, c_void_p, c_double, c_bool - ] - lib.MagickOpaquePaintImage.restype = c_bool - if is_im_6: - lib.MagickOpaquePaintImageChannel.argtypes = [ - c_void_p, c_int, c_void_p, c_void_p, c_double, c_bool - ] - lib.MagickOpaquePaintImageChannel.restype = c_bool - lib.MagickOptimizeImageLayers.argtypes = [c_void_p] - lib.MagickOptimizeImageLayers.restype = c_void_p - try: - lib.MagickOptimizeImageTransparency.argtypes = [c_void_p] - lib.MagickOptimizeImageTransparency.restype = c_bool - except AttributeError: - lib.MagickOptimizeImageTransparency = None - if is_im_7: - lib.MagickOrderedDitherImage.argtypes = [c_void_p, c_char_p] - lib.MagickOrderedDitherImage.restype = c_bool - if is_im_6: - lib.MagickOrderedPosterizeImage.argtypes = [c_void_p, c_char_p] - lib.MagickOrderedPosterizeImage.restype = c_bool - lib.MagickOrderedPosterizeImageChannel.argtypes = [ - c_void_p, c_int, c_char_p - ] - lib.MagickOrderedPosterizeImageChannel.restype = c_bool - lib.MagickPingImage.argtypes = [c_void_p, c_char_p] - lib.MagickPingImage.restype = c_bool - lib.MagickPingImageBlob.argtypes = [c_void_p, c_void_p, c_size_t] - lib.MagickPingImageBlob.restype = c_bool - lib.MagickPingImageFile.argtypes = [c_void_p, c_void_p] - lib.MagickPingImageFile.restype = c_bool - if is_im_6: - lib.MagickPolaroidImage.argtypes = [c_void_p, c_void_p, c_double] - lib.MagickPolaroidImage.restype = c_bool - else: - lib.MagickPolaroidImage.argtypes = [ - c_void_p, c_void_p, c_char_p, c_double, c_int - ] - lib.MagickPolaroidImage.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickPolynomialImage.argtypes = [c_void_p, c_size_t, - POINTER(c_double)] - lib.MagickPolynomialImage.restype = c_bool - except AttributeError: - lib.MagickPolynomialImage = None - else: - lib.MagickPolynomialImage = None - lib.MagickPosterizeImage.argtypes = [c_void_p, c_size_t, c_bool] - lib.MagickPosterizeImage.restype = c_bool - lib.MagickPreviewImages.argtypes = [c_void_p, c_int] - lib.MagickPreviewImages.restype = c_void_p - lib.MagickPreviousImage.argtypes = [c_void_p] - lib.MagickPreviousImage.restype = c_bool - if IM_VERSION < 0x700: - lib.MagickQuantizeImage.argtypes = [ - c_void_p, c_size_t, c_int, c_size_t, c_bool, c_bool - ] - lib.MagickQuantizeImage.restypes = c_bool - lib.MagickQuantizeImages.argtypes = [ - c_void_p, c_size_t, c_int, c_size_t, c_bool, c_bool - ] - lib.MagickQuantizeImages.restype = c_bool - else: - lib.MagickQuantizeImage.argtypes = [ - c_void_p, c_size_t, c_int, c_size_t, c_int, c_bool - ] - lib.MagickQuantizeImage.restypes = c_bool - lib.MagickQuantizeImages.argtypes = [ - c_void_p, c_size_t, c_int, c_size_t, c_int, c_bool - ] - lib.MagickQuantizeImages.restype = c_bool - lib.MagickRaiseImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t, c_bool - ] - lib.MagickRandomThresholdImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickRandomThresholdImage.restype = c_bool - if is_im_6: - lib.MagickRandomThresholdImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - lib.MagickRandomThresholdImageChannel.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickRangeThresholdImage.argtypes = [c_void_p, c_double, - c_double, c_double, - c_double] - lib.MagickRangeThresholdImage.restype = c_bool - except AttributeError: - lib.MagickRangeThresholdImage = None - else: - lib.MagickRangeThresholdImage = None - lib.MagickReadImage.argtypes = [c_void_p, c_char_p] - lib.MagickReadImage.restype = c_bool - lib.MagickReadImageBlob.argtypes = [c_void_p, c_void_p, c_size_t] - lib.MagickReadImageBlob.restype = c_bool - lib.MagickReadImageFile.argtypes = [c_void_p, c_void_p] - lib.MagickReadImageFile.restype = c_bool - try: - lib.MagickReduceNoiseImage.argtypes = [c_void_p, c_double] - lib.MagickReduceNoiseImage.restype = c_bool - except AttributeError: - pass - lib.MagickRemapImage.argtypes = [c_void_p, c_void_p, c_int] - lib.MagickRemapImage.restype = c_bool - lib.MagickRemoveImage.argtypes = [c_void_p] - lib.MagickRemoveImage.restype = c_bool - lib.MagickResampleImage.argtypes = [ - c_void_p, c_double, c_double, c_int, c_double - ] - lib.MagickResampleImage.restype = c_bool - lib.MagickResetImagePage.argtypes = [c_void_p, c_char_p] - lib.MagickResetImagePage.restype = c_bool - lib.MagickResizeImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_int, c_double - ] - lib.MagickResizeImage.restype = c_bool - lib.MagickRollImage.argtypes = [c_void_p, c_ssize_t, c_ssize_t] - lib.MagickRollImage.restype = c_bool - lib.MagickRotateImage.argtypes = [c_void_p, c_void_p, c_double] - lib.MagickRotateImage.restype = c_bool - try: - lib.MagickRotationalBlurImage.argtypes = [c_void_p, c_double] - lib.MagickRotationalBlurImage.restype = c_bool - if is_im_6: - lib.MagickRotationalBlurImageChannel.argtypes = [ - c_void_p, c_int, c_double - ] - lib.MagickRotationalBlurImageChannel.restype = c_bool - except AttributeError: - lib.MagickRotationalBlurImage = None - pass - lib.MagickSampleImage.argtypes = [c_void_p, c_size_t, c_size_t] - lib.MagickSampleImage.restype = c_bool - lib.MagickScaleImage.argtypes = [c_void_p, c_size_t, c_size_t] - lib.MagickScaleImage.restype = c_bool - lib.MagickSegmentImage.argtypes = [ - c_void_p, c_int, c_bool, c_double, c_double - ] - lib.MagickSegmentImage.restype = c_bool - lib.MagickSelectiveBlurImage.argtypes = [ - c_void_p, c_double, c_double, c_double - ] - lib.MagickSelectiveBlurImage.restype = c_bool - if is_im_6: - lib.MagickSelectiveBlurImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double, c_double - ] - lib.MagickSelectiveBlurImageChannel.restype = c_bool - lib.MagickSepiaToneImage.argtypes = [c_void_p, c_double] - lib.MagickSepiaToneImage.restype = c_bool - if is_im_6: - lib.MagickSeparateImage = None - lib.MagickSeparateImageChannel.argtypes = [c_void_p, c_int] - lib.MagickSeparateImageChannel.restype = c_bool - else: - lib.MagickSeparateImage.argtypes = [c_void_p, c_int] - lib.MagickSeparateImage.restype = c_bool - lib.MagickSeparateImageChannel = None - lib.MagickSetImage.argtypes = [c_void_p, c_void_p] - lib.MagickSetImage.restype = c_bool - lib.MagickSetImageAlphaChannel.argtypes = [c_void_p, c_int] - lib.MagickSetImageAlphaChannel.restype = c_bool - lib.MagickSetImageBackgroundColor.argtypes = [c_void_p, c_void_p] - lib.MagickSetImageBackgroundColor.restype = c_bool - if is_im_6: - lib.MagickSetImageBias.argtypes = [c_void_p, c_double] - lib.MagickSetImageBias.restype = c_bool - else: - lib.MagickSetImageBias = None - if is_im_6: - lib.MagickSetImageBluePrimary.argtypes = [ - c_void_p, c_double, c_double - ] - else: - lib.MagickSetImageBluePrimary.argtypes = [ - c_void_p, c_double, c_double, c_double - ] - lib.MagickSetImageBluePrimary.restype = c_bool - lib.MagickSetImageBorderColor.argtypes = [c_void_p, c_void_p] - lib.MagickSetImageBorderColor.restype = c_bool - if is_im_6: - lib.MagickSetImageChannelDepth.argtypes = [c_void_p, c_int, c_size_t] - lib.MagickSetImageClipMask.argtypes = [c_void_p, c_void_p] - lib.MagickSetImageClipMask.restype = c_bool - else: - lib.MagickSetImageChannelDepth = None - lib.MagickSetImageClipMask = None - if is_im_7: - lib.MagickSetImageChannelMask.argtypes = [c_void_p, c_int] - lib.MagickSetImageChannelMask.restype = c_int - lib.MagickSetImageColor.argtypes = [c_void_p, c_void_p] - lib.MagickSetImageColor.restype = c_bool - lib.MagickSetImageColormapColor.argtypes = [c_void_p, c_size_t, c_void_p] - lib.MagickSetImageColormapColor.restype = c_bool - lib.MagickSetImageColorspace.argtypes = [c_void_p, c_int] - lib.MagickSetImageColorspace.restype = c_bool - lib.MagickSetImageCompose.argtypes = [c_void_p, c_int] - lib.MagickSetImageCompose.restype = c_bool - lib.MagickSetImageCompression.argtypes = [c_void_p, c_int] - lib.MagickSetImageCompression.restype = c_bool - lib.MagickSetImageCompressionQuality.argtypes = [c_void_p, c_ssize_t] - lib.MagickSetImageCompressionQuality.restype = c_bool - lib.MagickSetImageDelay.argtypes = [c_void_p, c_ssize_t] - lib.MagickSetImageDelay.restype = c_bool - lib.MagickSetImageDepth.argtypes = [c_void_p, c_size_t] - lib.MagickSetImageDepth.restype = c_bool - lib.MagickSetImageDispose.argtypes = [c_void_p, c_int] - lib.MagickSetImageDispose.restype = c_bool - try: - lib.MagickSetImageEndian.argtypes = [c_void_p, c_int] - lib.MagickSetImageEndian.restype = c_bool - except AttributeError: - lib.MagickSetImageEndian = None - lib.MagickSetImageExtent.argtypes = [c_void_p, c_size_t, c_size_t] - lib.MagickSetImageExtent.restype = c_bool - lib.MagickSetImageFilename.argtypes = [c_void_p, c_char_p] - lib.MagickSetImageFilename.restype = c_bool - if IM_VERSION >= 0x710: - try: - lib.MagickSetImageFilter.argtypes = [c_void_p, c_int] - lib.MagickSetImageFilter.restype = c_bool - except AttributeError: - lib.MagickSetImageFilter = None - else: - lib.MagickSetImageFilter = None - lib.MagickSetImageFormat.argtypes = [c_void_p, c_char_p] - lib.MagickSetImageFormat.restype = c_bool - lib.MagickSetImageFuzz.argtypes = [c_void_p, c_double] - lib.MagickSetImageFuzz.restype = c_bool - lib.MagickSetImageGamma.argtypes = [c_void_p, c_double] - lib.MagickSetImageGamma.restype = c_bool - lib.MagickSetImageGravity.argtypes = [c_void_p, c_int] - lib.MagickSetImageGravity.restype = c_bool - if is_im_6: - lib.MagickSetImageGreenPrimary.argtypes = [ - c_void_p, c_double, c_double - ] - else: - lib.MagickSetImageGreenPrimary.argtypes = [ - c_void_p, c_double, c_double, c_double - ] - lib.MagickSetImageGreenPrimary.restype = c_bool - lib.MagickSetImageInterlaceScheme.argtypes = [c_void_p, c_int] - lib.MagickSetImageInterlaceScheme.restype = c_bool - lib.MagickSetImageInterpolateMethod.argtypes = [c_void_p, c_int] - lib.MagickSetImageInterpolateMethod.restype = c_bool - lib.MagickSetImageIterations.argtypes = [c_void_p, c_size_t] - lib.MagickSetImageIterations.restype = c_bool - if is_im_7: - lib.MagickSetImageMask.argtypes = [c_void_p, c_int, c_void_p] - lib.MagickSetImageMask.restype = c_bool - else: - lib.MagickSetImageMask = None - lib.MagickSetImageMatte.argtypes = [c_void_p, c_bool] - lib.MagickSetImageMatteColor.argtypes = [c_void_p, c_void_p] - if is_im_6: - lib.MagickSetImageOpacity.argtypes = [c_void_p, c_double] - lib.MagickSetImageOpacity.restype = c_bool - else: - lib.MagickSetImageOpacity = None - lib.MagickSetImageOrientation.argtypes = [c_void_p, c_int] - lib.MagickSetImagePage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickSetImagePage.restype = c_int - lib.MagickSetImageProgressMonitor.argtypes = [ - c_void_p, MagickProgressMonitor, c_void_p - ] - if is_im_6: - lib.MagickSetImageRedPrimary.argtypes = [ - c_void_p, c_double, c_double - ] - else: - lib.MagickSetImageRedPrimary.argtypes = [ - c_void_p, c_double, c_double, c_double - ] - lib.MagickSetImageRedPrimary.restype = c_bool - lib.MagickSetImageRenderingIntent.argtypes = [c_void_p, c_int] - lib.MagickSetImageRenderingIntent.restype = c_bool - lib.MagickSetImageResolution.argtypes = [c_void_p, c_double, c_double] - lib.MagickSetImageScene.argtypes = [c_void_p, c_size_t] - lib.MagickSetImageScene.restype = c_bool - lib.MagickSetImageTicksPerSecond.argtypes = [c_void_p, c_ssize_t] - lib.MagickSetImageTicksPerSecond.restype = c_bool - lib.MagickSetImageType.argtypes = [c_void_p, c_int] - lib.MagickSetImageUnits.argtypes = [c_void_p, c_int] - lib.MagickSetImageVirtualPixelMethod.argtypes = [c_void_p, c_int] - if is_im_6: - lib.MagickSetImageWhitePoint.argtypes = [ - c_void_p, c_double, c_double - ] - else: - lib.MagickSetImageWhitePoint.argtypes = [ - c_void_p, c_double, c_double, c_double - ] - lib.MagickSetImageWhitePoint.restype = c_bool - lib.MagickShadeImage.argtypes = [c_void_p, c_bool, c_double, c_double] - lib.MagickShadeImage.restype = c_bool - lib.MagickShadowImage.argtypes = [ - c_void_p, c_double, c_double, c_ssize_t, c_ssize_t - ] - lib.MagickShadowImage.restype = c_bool - lib.MagickSharpenImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickSharpenImage.restype = c_bool - if is_im_6: - lib.MagickSharpenImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double - ] - lib.MagickSharpenImageChannel.restype = c_bool - else: - lib.MagickSharpenImageChannel = None - lib.MagickShaveImage.argtypes = [c_void_p, c_size_t, c_size_t] - lib.MagickShaveImage.restype = c_void_p - lib.MagickShearImage.argtypes = [c_void_p, c_void_p, c_double, c_double] - lib.MagickShearImage.restype = c_bool - lib.MagickSigmoidalContrastImage.argtypes = [ - c_void_p, c_bool, c_double, c_double - ] - lib.MagickSigmoidalContrastImage.restype = c_bool - if is_im_6: - lib.MagickSigmoidalContrastImageChannel.argtypes = [ - c_void_p, c_int, c_bool, c_double, c_double - ] - lib.MagickSigmoidalContrastImageChannel.restype = c_bool - else: - lib.MagickSigmoidalContrastImageChannel = None - if is_im_6: - lib.MagickSimilarityImage.argtypes = [ - c_void_p, c_void_p, c_void_p, POINTER(c_double) - ] - lib.MagickSimilarityImage.restype = c_void_p - else: - lib.MagickSimilarityImage.argtypes = [ - c_void_p, c_void_p, c_int, c_double, c_void_p, POINTER(c_double) - ] - lib.MagickSimilarityImage.restype = c_void_p - lib.MagickSketchImage.argtypes = [c_void_p, c_double, c_double, c_double] - lib.MagickSketchImage.restype = c_bool - lib.MagickSmushImages.argtypes = [c_void_p, c_bool, c_ssize_t] - lib.MagickSmushImages.restype = c_void_p - lib.MagickSolarizeImage.argtypes = [c_void_p, c_double] - lib.MagickSolarizeImage.restype = c_bool - try: - lib.MagickSolarizeImageChannel.argtypes = [c_void_p, c_int, c_double] - lib.MagickSolarizeImageChannel.restype = c_bool - except AttributeError: - lib.MagickSolarizeImageChannel = None - if is_im_6: - lib.MagickSparseColorImage.argtypes = [ - c_void_p, c_int, c_int, c_size_t, POINTER(c_double) - ] - else: - lib.MagickSparseColorImage.argtypes = [ - c_void_p, c_int, c_size_t, POINTER(c_double) - ] - lib.MagickSparseColorImage.restype = c_bool - lib.MagickSpliceImage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickSpliceImage.restype = c_bool - if is_im_6: - lib.MagickSpreadImage.argtypes = [c_void_p, c_double] - lib.MagickSpreadImage.restype = c_bool - else: - lib.MagickSpreadImage.argtypes = [c_void_p, c_int, c_double] - lib.MagickSpreadImage.restype = c_bool - lib.MagickStatisticImage.argtypes = [c_void_p, c_int, c_size_t, c_size_t] - lib.MagickStatisticImage.restype = c_bool - if is_im_6: - try: - # TODO - Arguments for MagickStatisticImageChannel changed - # around commit 2d8a006b @ Feb 9 13:02:53 2013. Use IM_VERSION - # to determine correct method signature/arguments. - lib.MagickStatisticImageChannel.argtypes = [ - c_void_p, c_int, c_int, c_size_t, c_size_t - ] - lib.MagickStatisticImageChannel.restype = c_bool - except AttributeError: - lib.MagickStatisticImageChannel = None - else: - lib.MagickStatisticImageChannel = None - lib.MagickSteganoImage.argtypes = [c_void_p, c_void_p, c_ssize_t] - lib.MagickSteganoImage.restype = c_void_p - lib.MagickStereoImage.argtypes = [c_void_p, c_void_p] - lib.MagickStereoImage.restype = c_void_p - lib.MagickStripImage.argtypes = [c_void_p] - lib.MagickStripImage.restype = c_bool - if is_im_6: - lib.MagickSwirlImage.argtypes = [c_void_p, c_double] - lib.MagickSwirlImage.restype = c_bool - else: - lib.MagickSwirlImage.argtypes = [c_void_p, c_double, c_int] - lib.MagickSwirlImage.restype = c_bool - lib.MagickTextureImage.argtypes = [c_void_p, c_void_p] - lib.MagickTextureImage.restype = c_void_p - lib.MagickTintImage.argtypes = [c_void_p, c_void_p, c_void_p] - lib.MagickTintImage.restype = c_bool - lib.MagickThresholdImage.argtypes = [c_void_p, c_double] - lib.MagickThresholdImage.restype = c_bool - lib.MagickThresholdImageChannel.argtypes = [c_void_p, c_int, c_double] - lib.MagickThresholdImageChannel.restype = c_bool - lib.MagickThumbnailImage.argtypes = [c_void_p, c_size_t, c_size_t] - lib.MagickThumbnailImage.restype = c_bool - if is_im_6: - lib.MagickTransformImage.argtypes = [c_void_p, c_char_p, c_char_p] - lib.MagickTransformImage.restype = c_void_p - else: - lib.MagickTransformImage = None - lib.MagickTransformImageColorspace.argtypes = [c_void_p, c_int] - lib.MagickTransformImageColorspace.restype = c_bool - lib.MagickTransparentPaintImage.argtypes = [ - c_void_p, c_void_p, c_double, c_double, c_int - ] - lib.MagickTransparentPaintImage.restype = c_bool - lib.MagickTransposeImage.argtypes = [c_void_p] - lib.MagickTransposeImage.restype = c_bool - lib.MagickTransverseImage.argtypes = [c_void_p] - lib.MagickTransverseImage.restype = c_bool - lib.MagickTrimImage.argtypes = [c_void_p, c_double] - lib.MagickTrimImage.restype = c_bool - lib.MagickUniqueImageColors.argtypes = [c_void_p] - lib.MagickUniqueImageColors.restype = c_bool - lib.MagickUnsharpMaskImage.argtypes = [ - c_void_p, c_double, c_double, c_double, c_double - ] - lib.MagickUnsharpMaskImage.restype = c_bool - if is_im_6: - lib.MagickUnsharpMaskImageChannel.argtypes = [ - c_void_p, c_int, c_double, c_double, c_double, c_double - ] - lib.MagickUnsharpMaskImageChannel.restype = c_bool - else: - lib.MagickUnsharpMaskImageChannel = None - lib.MagickVignetteImage.argtypes = [ - c_void_p, c_double, c_double, c_ssize_t, c_ssize_t - ] - lib.MagickVignetteImage.restype = c_bool - if is_im_6: - lib.MagickWaveImage.argtypes = [c_void_p, c_double, c_double] - lib.MagickWaveImage.restype = c_bool - else: - lib.MagickWaveImage.argtypes = [c_void_p, c_double, c_double, c_int] - lib.MagickWaveImage.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickWaveletDenoiseImage.argtypes = [c_void_p, c_double, - c_double] - lib.MagickWaveletDenoiseImage.restype = c_bool - except AttributeError: - lib.MagickWaveletDenoiseImage = None - else: - lib.MagickWaveletDenoiseImage = None - if IM_VERSION > 0x709: - try: - lib.MagickWhiteBalanceImage.argtypes = [c_void_p] - lib.MagickWhiteBalanceImage.restype = c_bool - except AttributeError: - lib.MagickWhiteBalanceImage = None - else: - lib.MagickWhiteBalanceImage = None - lib.MagickWhiteThresholdImage.argtypes = [c_void_p, c_void_p] - lib.MagickWhiteThresholdImage.restype = c_bool - lib.MagickWriteImage.argtypes = [c_void_p, c_char_p] - lib.MagickWriteImage.restype = c_bool - lib.MagickWriteImageFile.argtypes = [c_void_p, c_void_p] - lib.MagickWriteImageFile.restype = c_bool - lib.MagickWriteImages.argtypes = [c_void_p, c_char_p, c_int] - lib.MagickWriteImages.restype = c_bool - lib.MagickWriteImagesFile.argtypes = [c_void_p, c_void_p] - lib.MagickWriteImagesFile.restype = c_bool diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_property.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_property.py deleted file mode 100644 index e990ad7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_property.py +++ /dev/null @@ -1,199 +0,0 @@ -""":mod:`wand.cdefs.magick_property` --- Magick-Property definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import (POINTER, c_void_p, c_char_p, c_size_t, c_ubyte, c_uint, - c_int, c_ulong, c_double, c_bool, c_ulonglong) -from wand.cdefs.wandtypes import c_ssize_t - -__all__ = ('load',) - - -def load(lib, IM_VERSION): - """Define Magick Wand property methods. The ImageMagick version is given as - a second argument for comparison. This will quick to determine which - methods are available from the library, and can be implemented as:: - - if IM_VERSION < 0x700: - # ... do ImageMagick-6 methods ... - else - # ... do ImageMagick-7 methods ... - - .. seealso:: - - #include "wand/magick-property.h" - // Or - #include "MagickWand/magick-property.h" - - :param lib: the loaded ``MagickWand`` library - :type lib: :class:`ctypes.CDLL` - :param IM_VERSION: the ImageMagick version number (i.e. 0x0689) - :type IM_VERSION: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - - """ - lib.MagickDeleteImageArtifact.argtypes = [c_void_p, c_char_p] - lib.MagickDeleteImageArtifact.restype = c_bool - lib.MagickDeleteImageProperty.argtypes = [c_void_p, c_char_p] - lib.MagickDeleteImageProperty.restype = c_bool - lib.MagickDeleteOption.argtypes = [c_void_p, c_char_p] - lib.MagickDeleteOption.restype = c_bool - lib.MagickGetAntialias.argtypes = [c_void_p] - lib.MagickGetAntialias.restype = c_bool - lib.MagickGetBackgroundColor.argtypes = [c_void_p] - lib.MagickGetBackgroundColor.restype = c_void_p - lib.MagickGetColorspace.argtypes = [c_void_p] - lib.MagickGetColorspace.restype = c_int - lib.MagickGetCompression.argtypes = [c_void_p] - lib.MagickGetCompression.restype = c_int - lib.MagickGetCompressionQuality.argtypes = [c_void_p] - lib.MagickGetCompressionQuality.restype = c_size_t - lib.MagickGetFont.argtypes = [c_void_p] - lib.MagickGetFont.restype = c_void_p - if IM_VERSION >= 0x710: - try: - lib.MagickGetFilter.argtypes = [c_void_p] - lib.MagickGetFilter.restype = c_int - except AttributeError: - lib.MagickGetFilter = None - else: - lib.MagickGetFilter = None - lib.MagickGetGravity.argtypes = [c_void_p] - lib.MagickGetGravity.restype = c_int - lib.MagickGetImageArtifact.argtypes = [c_void_p, c_char_p] - lib.MagickGetImageArtifact.restype = c_void_p - lib.MagickGetImageArtifacts.argtypes = [ - c_void_p, c_char_p, POINTER(c_size_t) - ] - lib.MagickGetImageArtifacts.restype = POINTER(c_void_p) - lib.MagickGetImageProfile.argtypes = [ - c_void_p, c_char_p, POINTER(c_size_t) - ] - lib.MagickGetImageProfile.restype = POINTER(c_ubyte) - lib.MagickGetImageProfiles.argtypes = [ - c_void_p, c_char_p, POINTER(c_size_t) - ] - lib.MagickGetImageProfiles.restype = POINTER(c_void_p) - lib.MagickGetImageProperty.argtypes = [c_void_p, c_char_p] - lib.MagickGetImageProperty.restype = c_void_p - lib.MagickGetImageProperties.argtypes = [ - c_void_p, c_char_p, POINTER(c_size_t) - ] - lib.MagickGetImageProperties.restype = POINTER(c_void_p) - lib.MagickGetInterlaceScheme.argtypes = [c_void_p] - lib.MagickGetInterlaceScheme.restype = c_int - lib.MagickGetOption.argtypes = [c_void_p, c_char_p] - lib.MagickGetOption.restype = c_void_p - lib.MagickGetPointsize.argtypes = [c_void_p] - lib.MagickGetPointsize.restype = c_double - lib.MagickGetQuantumRange.argtypes = [POINTER(c_size_t)] - lib.MagickGetResource.argtypes = [c_int] - lib.MagickGetResource.restype = c_ulonglong - lib.MagickGetResourceLimit.argtypes = [c_int] - lib.MagickGetResourceLimit.restype = c_ulonglong - lib.MagickGetSamplingFactors.argtypes = [c_void_p, POINTER(c_size_t)] - lib.MagickGetSamplingFactors.restype = POINTER(c_double) - lib.MagickGetSize.argtypes = [c_void_p, POINTER(c_uint), POINTER(c_uint)] - lib.MagickGetSize.restype = c_int - lib.MagickQueryConfigureOption.argtypes = [c_char_p] - lib.MagickQueryConfigureOption.restype = c_void_p - lib.MagickQueryConfigureOptions.argtypes = [c_char_p, POINTER(c_size_t)] - lib.MagickQueryConfigureOptions.restype = POINTER(c_void_p) - lib.MagickQueryFontMetrics.argtypes = [c_void_p, c_void_p, c_char_p] - lib.MagickQueryFontMetrics.restype = POINTER(c_double) - lib.MagickQueryFonts.argtypes = [c_char_p, POINTER(c_size_t)] - lib.MagickQueryFonts.restype = POINTER(c_void_p) - lib.MagickQueryFormats.argtypes = [c_char_p, POINTER(c_size_t)] - lib.MagickQueryFormats.restype = POINTER(c_void_p) - lib.MagickQueryMultilineFontMetrics.argtypes = [ - c_void_p, c_void_p, c_char_p - ] - lib.MagickQueryMultilineFontMetrics.restype = POINTER(c_double) - lib.MagickRemoveImageProfile.argtypes = [ - c_void_p, c_char_p, POINTER(c_size_t) - ] - lib.MagickRemoveImageProfile.restype = POINTER(c_ubyte) - lib.MagickSetAntialias.argtypes = [c_void_p, c_int] - lib.MagickSetAntialias.restype = c_bool - lib.MagickSetBackgroundColor.argtypes = [c_void_p, c_void_p] - lib.MagickSetBackgroundColor.restype = c_bool - lib.MagickSetColorspace.argtypes = [c_void_p, c_int] - lib.MagickSetColorspace.restype = c_bool - lib.MagickSetCompression.argtypes = [c_void_p, c_int] - lib.MagickSetCompression.restype = c_bool - lib.MagickSetCompressionQuality.argtypes = [c_void_p, c_size_t] - lib.MagickSetCompressionQuality.restype = c_bool - lib.MagickSetDepth.argtypes = [c_void_p, c_uint] - lib.MagickSetDepth.restype = c_bool - lib.MagickSetExtract.argtypes = [c_void_p, c_char_p] - lib.MagickSetExtract.restype = c_bool - lib.MagickSetFilename.argtypes = [c_void_p, c_char_p] - lib.MagickSetFilename.restype = c_bool - if IM_VERSION >= 0x710: - try: - lib.MagickSetFilter.argtypes = [c_void_p, c_int] - lib.MagickSetFilter.restype = c_bool - except AttributeError: - lib.MagickSetFilter = None - else: - lib.MagickSetFilter = None - lib.MagickSetFilter - lib.MagickSetFont.argtypes = [c_void_p, c_char_p] - lib.MagickSetFont.restype = c_bool - lib.MagickSetFormat.argtypes = [c_void_p, c_char_p] - lib.MagickSetFormat.restype = c_bool - lib.MagickSetGravity.argtypes = [c_void_p, c_int] - lib.MagickSetGravity.restype = c_bool - lib.MagickSetImageArtifact.argtypes = [c_void_p, c_char_p, c_char_p] - lib.MagickSetImageProfile.argtypes = [ - c_void_p, c_char_p, c_void_p, c_size_t - ] - lib.MagickSetImageProfile.restype = c_bool - lib.MagickSetImageProperty.argtypes = [c_void_p, c_char_p, c_char_p] - lib.MagickSetImageProperty.restype = c_bool - lib.MagickSetInterlaceScheme.argtypes = [c_void_p, c_int] - lib.MagickSetInterlaceScheme.restype = c_bool - lib.MagickSetInterpolateMethod.argtypes = [c_void_p, c_int] - lib.MagickSetInterpolateMethod.restype = c_bool - lib.MagickSetOption.argtypes = [c_void_p, c_char_p, c_char_p] - lib.MagickSetOption.restype = c_bool - lib.MagickSetOrientation.argtypes = [c_void_p, c_int] - lib.MagickSetOrientation.restype = c_bool - lib.MagickSetPage.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t, c_ssize_t - ] - lib.MagickSetPage.restype = c_bool - lib.MagickSetPassphrase.argtypes = [c_void_p, c_char_p] - lib.MagickSetPassphrase.restype = c_bool - lib.MagickSetPointsize.argtypes = [c_void_p, c_double] - lib.MagickSetPointsize.restype = c_bool - lib.MagickSetResolution.argtypes = [c_void_p, c_double, c_double] - lib.MagickSetResolution.restype = c_bool - lib.MagickSetResourceLimit.argtypes = [c_int, c_ulonglong] - lib.MagickSetResourceLimit.restype = c_bool - lib.MagickSetSamplingFactors.argtypes = [ - c_void_p, c_size_t, POINTER(c_double) - ] - lib.MagickSetSamplingFactors.restype = c_bool - if IM_VERSION >= 0x708: - try: - lib.MagickSetSeed.argtypes = [c_void_p, c_ulong] - except AttributeError: - lib.MagickSetSeed = None - else: - lib.MagickSetSeed = None - try: - lib.MagickSetSecurityPolicy.argtypes = [c_void_p, c_char_p] - lib.MagickSetSecurityPolicy.restype = c_bool - except AttributeError: - lib.MagickSetSecurityPolicy = None - lib.MagickSetSize.argtypes = [c_void_p, c_uint, c_uint] - lib.MagickSetSize.restype = c_bool - lib.MagickSetSizeOffset.argtypes = [ - c_void_p, c_size_t, c_size_t, c_ssize_t - ] - lib.MagickSetSizeOffset.restype = c_bool - lib.MagickSetType.argtypes = [c_void_p, c_int] - lib.MagickSetType.restype = c_bool diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_wand.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_wand.py deleted file mode 100644 index ced282b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/magick_wand.py +++ /dev/null @@ -1,67 +0,0 @@ -""":mod:`wand.cdefs.magick_wand` --- Magick-Wand definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import POINTER, c_void_p, c_bool, c_int -from wand.cdefs.wandtypes import c_ssize_t - -__all__ = ('load',) - - -def load(lib, IM_VERSION): - """Define Magick Wand methods. The ImageMagick version is given as - a second argument for comparison. This will quick to determine which - methods are available from the library, and can be implemented as:: - - if IM_VERSION < 0x700: - # ... do ImageMagick-6 methods ... - else - # ... do ImageMagick-7 methods ... - - .. seealso:: - - #include "wand/magick-wand.h" - // Or - #include "MagickWand/magick-wand.h" - - :param lib: the loaded ``MagickWand`` library - :type lib: :class:`ctypes.CDLL` - :param IM_VERSION: the ImageMagick version number (i.e. 0x0689) - :type IM_VERSION: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - - """ - lib.ClearMagickWand.argtypes = [c_void_p] - lib.CloneMagickWand.argtypes = [c_void_p] - lib.CloneMagickWand.restype = c_void_p - lib.DestroyMagickWand.argtypes = [c_void_p] - lib.DestroyMagickWand.restype = c_void_p - lib.IsMagickWand.argtypes = [c_void_p] - try: - lib.IsMagickWandInstantiated.argtypes = [] - lib.IsMagickWandInstantiated.restype = c_bool - except AttributeError: - lib.IsMagickWandInstantiated = None - pass - lib.MagickClearException.argtypes = [c_void_p] - lib.MagickGetException.argtypes = [c_void_p, POINTER(c_int)] - lib.MagickGetException.restype = c_void_p - lib.MagickGetExceptionType.argtypes = [c_void_p] - lib.MagickGetExceptionType.restype = c_int - lib.MagickGetIteratorIndex.argtypes = [c_void_p] - lib.MagickGetIteratorIndex.restype = c_ssize_t - lib.MagickRelinquishMemory.argtypes = [c_void_p] - lib.MagickRelinquishMemory.restype = c_void_p - lib.MagickResetIterator.argtypes = [c_void_p] - lib.MagickSetFirstIterator.argtypes = [c_void_p] - lib.MagickSetIteratorIndex.argtypes = [c_void_p, c_ssize_t] - lib.MagickSetIteratorIndex.restype = c_bool - lib.MagickSetLastIterator.argtypes = [c_void_p] - lib.MagickWandGenesis.argtypes = [] - lib.MagickWandTerminus.argtypes = [] - lib.NewMagickWandFromImage.argtypes = [c_void_p] - lib.NewMagickWandFromImage.restype = c_void_p - lib.NewMagickWand.argtypes = [] - lib.NewMagickWand.restype = c_void_p diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/pixel_iterator.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/pixel_iterator.py deleted file mode 100644 index 7354116..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/pixel_iterator.py +++ /dev/null @@ -1,49 +0,0 @@ -""":mod:`wand.cdefs.pixel_iterator` --- Pixel-Iterator definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import POINTER, c_void_p, c_int, c_size_t -from wand.cdefs.wandtypes import c_ssize_t - -__all__ = ('load',) - - -def load(lib, IM_VERSION): - """Define Pixel Iterator methods. The ImageMagick version is given as - a second argument for comparison. This will quick to determine which - methods are available from the library, and can be implemented as:: - - if IM_VERSION < 0x700: - # ... do ImageMagick-6 methods ... - else - # ... do ImageMagick-7 methods ... - - .. seealso:: - - #include "wand/pixel-iterator.h" - // Or - #include "MagickWand/pixel-iterator.h" - - :param lib: the loaded ``MagickWand`` library - :type lib: :class:`ctypes.CDLL` - :param IM_VERSION: the ImageMagick version number (i.e. 0x0689) - :type IM_VERSION: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - - """ - lib.ClonePixelIterator.argtypes = [c_void_p] - lib.ClonePixelIterator.restype = c_void_p - lib.DestroyPixelIterator.argtypes = [c_void_p] - lib.DestroyPixelIterator.restype = c_void_p - lib.IsPixelIterator.argtypes = [c_void_p] - lib.NewPixelIterator.argtypes = [c_void_p] - lib.NewPixelIterator.restype = c_void_p - lib.PixelClearIteratorException.argtypes = [c_void_p] - lib.PixelGetIteratorException.argtypes = [c_void_p, POINTER(c_int)] - lib.PixelGetIteratorException.restype = c_void_p - lib.PixelGetNextIteratorRow.argtypes = [c_void_p, POINTER(c_size_t)] - lib.PixelGetNextIteratorRow.restype = POINTER(c_void_p) - lib.PixelSetFirstIteratorRow.argtypes = [c_void_p] - lib.PixelSetIteratorRow.argtypes = [c_void_p, c_ssize_t] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/pixel_wand.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/pixel_wand.py deleted file mode 100644 index cbd511b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/pixel_wand.py +++ /dev/null @@ -1,178 +0,0 @@ -""":mod:`wand.cdefs.pixel_wand` --- Pixel-Wand definitions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import (CDLL, POINTER, c_char_p, c_double, - c_float, c_int, c_longdouble, c_size_t, - c_ubyte, c_uint, c_ushort, c_void_p) -import numbers -import platform - -__all__ = ('load',) - - -def load(lib, IM_VERSION, IM_QUANTUM_DEPTH, IM_HDRI): - """Define Pixel Wand methods. The ImageMagick version is given as - a second argument for comparison. This will quick to determine which - methods are available from the library, and can be implemented as:: - - if IM_VERSION < 0x700: - # ... do ImageMagick-6 methods ... - else - # ... do ImageMagick-7 methods ... - - .. seealso:: - - #include "wand/pixel-wand.h" - // Or - #include "MagickWand/pixel-wand.h" - - Mapping Pixel methods also requires the wand library to evaluate - what "Quantum" is to ImageMagick. We must query the library - to identify if HDRI is enabled, and what the quantum depth is. - - .. seealso:: - - MagickCore/magick-type.h - - :param lib: the loaded ``MagickWand`` library. - :type lib: :class:`ctypes.CDLL` - :param IM_VERSION: the ImageMagick version number (i.e. 0x0689). - :type IM_VERSION: :class:`numbers.Integral` - :param IM_QUANTUM_DEPTH: the ImageMagick Quantum Depth - (must be 8, 16, 32, or 64). - :type IM_QUANTUM_DEPTH: :class:`numbers.Integral` - :param IM_HDRI: if ImageMagick was compiled with HDRI support. - :type IM_HDRI: :class:`bool` - - .. versionadded:: 0.5.0 - - """ - if not isinstance(lib, CDLL): - raise AttributeError(repr(lib) + " is not an instanced of ctypes.CDLL") - if not isinstance(IM_VERSION, numbers.Integral): - raise AttributeError("Expecting MagickCore version number") - if IM_QUANTUM_DEPTH not in [8, 16, 32, 65]: - raise AttributeError("QUANTUM_DEPTH must be one of 8, 16, 32, or 64") - is_im_6 = IM_VERSION < 0x700 - is_im_7 = IM_VERSION >= 0x700 - - # Check for IBM Z Systems, or where `double_t` is defined. - if platform.machine() in ['s390', 's390x', 'i686']: - FloatType = c_double - else: - FloatType = c_float - - if IM_QUANTUM_DEPTH == 8: - QuantumType = FloatType if IM_HDRI else c_ubyte - elif IM_QUANTUM_DEPTH == 16: - QuantumType = FloatType if IM_HDRI else c_ushort - elif IM_QUANTUM_DEPTH == 32: - QuantumType = c_double if IM_HDRI else c_uint - elif IM_QUANTUM_DEPTH == 64: - QuantumType = c_longdouble - - lib.ClearPixelWand.argtypes = [c_void_p] - lib.ClonePixelWand.argtypes = [c_void_p] - lib.ClonePixelWand.restype = c_void_p - lib.DestroyPixelWand.argtypes = [c_void_p] - lib.DestroyPixelWand.restype = c_void_p - lib.DestroyPixelWands.argtypes = [POINTER(c_void_p), c_size_t] - lib.DestroyPixelWands.restype = POINTER(c_void_p) - lib.IsPixelWand.argtypes = [c_void_p] - lib.IsPixelWandSimilar.argtypes = [c_void_p, c_void_p, c_double] - lib.NewPixelWand.argtypes = [] - lib.NewPixelWand.restype = c_void_p - lib.PixelClearException.argtypes = [c_void_p] - lib.PixelClearException.restype = c_int - lib.PixelGetAlpha.argtypes = [c_void_p] - lib.PixelGetAlpha.restype = c_double - lib.PixelGetAlphaQuantum.argtypes = [c_void_p] - lib.PixelGetAlphaQuantum.restype = QuantumType - lib.PixelGetBlack.argtypes = [c_void_p] - lib.PixelGetBlack.restype = c_double - lib.PixelGetBlackQuantum.argtypes = [c_void_p] - lib.PixelGetBlackQuantum.restype = QuantumType - lib.PixelGetBlue.argtypes = [c_void_p] - lib.PixelGetBlue.restype = c_double - lib.PixelGetBlueQuantum.argtypes = [c_void_p] - lib.PixelGetBlueQuantum.restype = QuantumType - lib.PixelGetColorAsNormalizedString.argtypes = [c_void_p] - lib.PixelGetColorAsNormalizedString.restype = c_void_p - lib.PixelGetColorAsString.argtypes = [c_void_p] - lib.PixelGetColorAsString.restype = c_void_p - lib.PixelGetColorCount.argtypes = [c_void_p] - lib.PixelGetColorCount.restype = c_size_t - lib.PixelGetCyan.argtypes = [c_void_p] - lib.PixelGetCyan.restype = c_double - lib.PixelGetCyanQuantum.argtypes = [c_void_p] - lib.PixelGetCyanQuantum.restype = QuantumType - lib.PixelGetException.argtypes = [c_void_p, POINTER(c_int)] - lib.PixelGetException.restype = c_void_p - lib.PixelGetExceptionType.argtypes = [c_void_p] - lib.PixelGetExceptionType.restype = c_int - lib.PixelGetFuzz.argtypes = [c_void_p] - lib.PixelGetFuzz.restype = c_double - lib.PixelGetGreen.argtypes = [c_void_p] - lib.PixelGetGreen.restype = c_double - lib.PixelGetGreenQuantum.argtypes = [c_void_p] - lib.PixelGetGreenQuantum.restype = QuantumType - lib.PixelGetHSL.argtypes = [c_void_p, - POINTER(c_double), - POINTER(c_double), - POINTER(c_double)] - lib.PixelGetIndex.argtypes = [c_void_p] - lib.PixelGetIndex.restype = QuantumType - lib.PixelGetMagenta.argtypes = [c_void_p] - lib.PixelGetMagenta.restype = c_double - lib.PixelGetMagentaQuantum.argtypes = [c_void_p] - lib.PixelGetMagentaQuantum.restype = QuantumType - lib.PixelGetMagickColor.argtypes = [c_void_p, c_void_p] - if is_im_7: - lib.PixelGetPixel.argtypes = [c_void_p] - lib.PixelGetPixel.restype = c_void_p - lib.PixelGetRed.argtypes = [c_void_p] - lib.PixelGetRed.restype = c_double - lib.PixelGetRedQuantum.argtypes = [c_void_p] - lib.PixelGetRedQuantum.restype = QuantumType - lib.PixelGetYellow.argtypes = [c_void_p] - lib.PixelGetYellow.restype = c_double - lib.PixelGetYellowQuantum.argtypes = [c_void_p] - lib.PixelGetYellowQuantum.restype = QuantumType - lib.PixelSetAlpha.argtypes = [c_void_p, c_double] - lib.PixelSetAlphaQuantum.argtypes = [c_void_p, QuantumType] - lib.PixelSetBlack.argtypes = [c_void_p, c_double] - lib.PixelSetBlackQuantum.argtypes = [c_void_p, QuantumType] - lib.PixelSetBlue.argtypes = [c_void_p, c_double] - lib.PixelSetBlueQuantum.argtypes = [c_void_p, QuantumType] - lib.PixelSetColor.argtypes = [c_void_p, c_char_p] - lib.PixelSetColor.restype = c_int - lib.PixelSetColorCount.argtypes = [c_void_p, c_size_t] - lib.PixelSetCyan.argtypes = [c_void_p, c_double] - lib.PixelSetCyanQuantum.argtypes = [c_void_p, QuantumType] - lib.PixelSetFuzz.argtypes = [c_void_p, c_double] - lib.PixelSetGreen.argtypes = [c_void_p, c_double] - lib.PixelSetGreenQuantum.argtypes = [c_void_p, QuantumType] - lib.PixelSetHSL.argtypes = [c_void_p, c_double, c_double, c_double] - lib.PixelSetIndex.argtypes = [c_void_p, QuantumType] - lib.PixelSetMagenta.argtypes = [c_void_p, c_double] - lib.PixelSetMagentaQuantum.argtypes = [c_void_p, QuantumType] - if is_im_6: - lib.PixelSetMagickColor.argtypes = [c_void_p, c_void_p] - else: - lib.PixelSetMagickColor = None - if is_im_7: - lib.PixelSetPixelColor.argtypes = [c_void_p, c_void_p] - else: - lib.PixelSetPixelColor = None - lib.PixelSetRed.argtypes = [c_void_p, c_double] - lib.PixelSetRedQuantum.argtypes = [c_void_p, QuantumType] - lib.PixelSetYellow.argtypes = [c_void_p, c_double] - lib.PixelSetYellowQuantum.argtypes = [c_void_p, QuantumType] - if is_im_6: - lib.PixelSetMagickColor.argtypes = [c_void_p, c_void_p] - lib.PixelSetPixelColor = None - if is_im_7: - lib.PixelSetMagickColor = None - lib.PixelSetPixelColor.argtypes = [c_void_p, c_void_p] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/structures.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/structures.py deleted file mode 100644 index 0e86ee1..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/structures.py +++ /dev/null @@ -1,220 +0,0 @@ -""":mod:`wand.cdefs.structures` --- MagickWand C-Structures -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -from ctypes import POINTER, Structure, c_bool, c_double, c_int, c_size_t -from wand.cdefs.wandtypes import c_ssize_t, c_magick_real_t, c_magick_size_t - -__all__ = ('AffineMatrix', 'CCMaxMetrics', 'CCObjectInfo', 'CCObjectInfo70A', - 'CCObjectInfo710', 'ChannelFeature', 'GeometryInfo', 'KernelInfo', - 'MagickPixelPacket', 'PixelInfo', 'PointInfo', 'RectangleInfo') - - -class AffineMatrix(Structure): - - _fields_ = [('sx', c_double), - ('rx', c_double), - ('ry', c_double), - ('sy', c_double), - ('tx', c_double), - ('ty', c_double)] - - -class ChannelFeature(Structure): - """ - /* - Haralick texture features. - */ - typedef struct _ChannelFeatures - { - double - angular_second_moment[4], - contrast[4], - correlation[4], - variance_sum_of_squares[4], - inverse_difference_moment[4], - sum_average[4], - sum_variance[4], - sum_entropy[4], - entropy[4], - difference_variance[4], - difference_entropy[4], - measure_of_correlation_1[4], - measure_of_correlation_2[4], - maximum_correlation_coefficient[4]; - } ChannelFeatures; - """ - _fields_ = [('angular_second_moment', c_double * 4), - ('contrast', c_double * 4), - ('correlation', c_double * 4), - ('variance_sum_of_squares', c_double * 4), - ('inverse_difference_moment', c_double * 4), - ('sum_average', c_double * 4), - ('sum_variance', c_double * 4), - ('sum_entropy', c_double * 4), - ('entropy', c_double * 4), - ('difference_variance', c_double * 4), - ('difference_entropy', c_double * 4), - ('measure_of_correlation_1', c_double * 4), - ('measure_of_correlation_2', c_double * 4), - ('maximum_correlation_coefficient', c_double * 4)] - - -class GeometryInfo(Structure): - NoValue = 0x0000 - XValue = 0x0001 - XiValue = 0x0001 - YValue = 0x0002 - PsiValue = 0x0002 - WidthValue = 0x0004 - RhoValue = 0x0004 - HeightValue = 0x0008 - SigmaValue = 0x0008 - ChiValue = 0x0010 - XiNegative = 0x0020 - XNegative = 0x0020 - PsiNegative = 0x0040 - YNegative = 0x0040 - ChiNegative = 0x0080 - PercentValue = 0x1000 - AspectValue = 0x2000 - NormalizeValue = 0x2000 - LessValue = 0x4000 - GreaterValue = 0x8000 - MinimumValue = 0x10000 - CorrelateNormalizeValue = 0x10000 - AreaValue = 0x20000 - DecimalValue = 0x40000 - SeparatorValue = 0x80000 - AspectRatioValue = 0x100000 - AllValues = 0x7fffffff - _fields_ = [('rho', c_double), - ('sigma', c_double), - ('xi', c_double), - ('psi', c_double), - ('chi', c_double)] - - -class KernelInfo(Structure): - pass - - -KernelInfo._fields_ = [('type', c_int), - ('width', c_size_t), - ('height', c_size_t), - ('x', c_ssize_t), - ('y', c_ssize_t), - ('values', POINTER(c_double)), - ('minimum', c_double), - ('maximum', c_double), - ('negative_range', c_double), - ('positive_range', c_double), - ('angle', c_double), - ('next', POINTER(KernelInfo)), - ('signature', c_size_t)] - - -class MagickPixelPacket(Structure): - - _fields_ = [('storage_class', c_int), - ('colorspace', c_int), - ('matte', c_int), - ('fuzz', c_double), - ('depth', c_size_t), - ('red', c_magick_real_t), - ('green', c_magick_real_t), - ('blue', c_magick_real_t), - ('opacity', c_magick_real_t), - ('index', c_magick_real_t)] - - -class OffsetInfo(Structure): - - _fields_ = [('x', c_double), - ('y', c_double)] - - -class PixelInfo(Structure): - - _fields_ = [('storage_class', c_int), - ('colorspace', c_int), - ('alpha_trait', c_int), - ('fuzz', c_double), - ('depth', c_size_t), - ('count', c_magick_size_t), - ('red', c_magick_real_t), - ('green', c_magick_real_t), - ('blue', c_magick_real_t), - ('black', c_magick_real_t), - ('alpha', c_magick_real_t), - ('index', c_magick_real_t)] - - -class PointInfo(Structure): - - _fields_ = [('x', c_double), - ('y', c_double)] - - -class RectangleInfo(Structure): - - _fields_ = [('width', c_size_t), - ('height', c_size_t), - ('x', c_ssize_t), - ('y', c_ssize_t)] - - -class CCObjectInfo(Structure): - _fields_ = [('_id', c_ssize_t), - ('bounding_box', RectangleInfo), - ('color', PixelInfo), - ('centroid', PointInfo), - ('area', c_double), - ('census', c_double)] - - -CCMaxMetrics = 16 - - -class CCObjectInfo70A(Structure): - CCMaxMetrics = CCMaxMetrics - _fields_ = [('_id', c_ssize_t), - ('bounding_box', RectangleInfo), - ('color', PixelInfo), - ('centroid', PointInfo), - ('area', c_double), - ('census', c_double), - ('merge', c_bool), - ('metric', c_double * CCMaxMetrics)] - - -class CCObjectInfo710(Structure): - CCMaxMetrics = CCMaxMetrics - _fields_ = [('_id', c_ssize_t), - ('bounding_box', RectangleInfo), - ('color', PixelInfo), - ('centroid', PointInfo), - ('area', c_double), - ('census', c_double), - ('merge', c_bool), - ('metric', c_double * CCMaxMetrics), - ('key', c_ssize_t)] - - -# All this will change with IM7, so let's not implement this just yet. -# -# class ImageChannelStatistics(Structure): -# _fields_ = [('maximum', c_double), -# ('minimum', c_double), -# ('mean', c_double), -# ('standard_deviation', c_double), -# ('variance', c_double), -# ('kurtosis', c_double), -# ('skewness', c_double)] -# -# class ImageStatistics(Structure): -# _fields_ = [('red', ImageChannelStatistics), -# ('green', ImageChannelStatistics), -# ('blue', ImageChannelStatistics), -# ('opacity', ImageChannelStatistics)] diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/wandtypes.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/wandtypes.py deleted file mode 100644 index 63dd26b..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/cdefs/wandtypes.py +++ /dev/null @@ -1,44 +0,0 @@ -""":mod:`wand.cdefs.structures` --- MagickWand C typedefs -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.5.0 -""" -import ctypes -import os -import sys - -__all__ = ('c_magick_real_t', 'c_magick_size_t', 'c_ssize_t') - - -if not hasattr(ctypes, 'c_ssize_t'): - if ctypes.sizeof(ctypes.c_uint) == ctypes.sizeof(ctypes.c_void_p): - ctypes.c_ssize_t = ctypes.c_int - elif ctypes.sizeof(ctypes.c_ulong) == ctypes.sizeof(ctypes.c_void_p): - ctypes.c_ssize_t = ctypes.c_long - elif ctypes.sizeof(ctypes.c_ulonglong) == ctypes.sizeof(ctypes.c_void_p): - ctypes.c_ssize_t = ctypes.c_longlong -c_ssize_t = ctypes.c_ssize_t - - -env_real = os.getenv('WAND_REAL_TYPE', 'auto') -if env_real in ('double', 'c_double'): - c_magick_real_t = ctypes.c_double -elif env_real in ('longdouble', 'c_longdouble'): - c_magick_real_t = ctypes.c_longdouble -else: - # Attempt to guess MagickRealType size - if sys.maxsize > 2**32: - c_magick_real_t = ctypes.c_double - else: - c_magick_real_t = ctypes.c_longdouble -del env_real - - -# FIXME: Might need to rewrite to check against c_void_p size; -# like `c_ssize_t` above, and not against window platform. -if ctypes.sizeof(ctypes.c_size_t) == 8: - c_magick_size_t = ctypes.c_size_t -elif ctypes.sizeof(ctypes.c_ulonglong) == 8: - c_magick_size_t = ctypes.c_ulonglong -else: - c_magick_size_t = ctypes.c_uint64 diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/color.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/color.py deleted file mode 100644 index 47fc8cc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/color.py +++ /dev/null @@ -1,779 +0,0 @@ -""":mod:`wand.color` --- Colors -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.1.2 - -""" -import ctypes -import numbers - -from .api import library -from .cdefs.structures import MagickPixelPacket, PixelInfo -from .compat import binary, text -from .resource import Resource -from .version import MAGICK_VERSION_NUMBER, MAGICK_HDRI, QUANTUM_DEPTH - -__all__ = 'Color', 'scale_quantum_to_int8' - - -class Color(Resource): - """Color value. - - Unlike any other objects in Wand, its resource management can be - implicit when it used outside of :keyword:`with` block. In these case, - its resource are allocated for every operation which requires a resource - and destroyed immediately. Of course it is inefficient when the - operations are much, so to avoid it, you should use color objects - inside of :keyword:`with` block explicitly e.g.:: - - red_count = 0 - with Color('#f00') as red: - with Image(filename='image.png') as img: - for row in img: - for col in row: - if col == red: - red_count += 1 - - :param string: a color name string e.g. ``'rgb(255, 255, 255)'``, - ``'#fff'``, ``'white'``. see `ImageMagick Color Names`_ - doc also - :type string: :class:`basestring` - - .. versionchanged:: 0.3.0 - :class:`Color` objects become hashable. - - .. versionchanged:: 0.5.1 - Color channel properties can now be set. - - .. versionchanged:: 0.5.1 - Added :attr:`cyan`, :attr:`magenta`, :attr:`yellow`, & :attr:`black` - properties for CMYK :class:`Color` instances. - - .. versionchanged:: 0.5.1 - Method :meth:`Color.from_hsl()` can create a RGB color from ``hue``, - ``saturation``, & ``lightness`` values. - - .. seealso:: - - `ImageMagick Color Names`_ - The color can then be given as a color name (there is a limited - but large set of these; see below) or it can be given as a set - of numbers (in decimal or hexadecimal), each corresponding to - a channel in an RGB or RGBA color model. HSL, HSLA, HSB, HSBA, - CMYK, or CMYKA color models may also be specified. These topics - are briefly described in the sections below. - - .. _ImageMagick Color Names: http://www.imagemagick.org/script/color.php - - .. describe:: == (other) - - Equality operator. - - :param other: a color another one - :type color: :class:`Color` - :returns: ``True`` only if two images equal. - :rtype: :class:`bool` - - """ - - #: (:class:`bool`) Whether the color has changed or not. - dirty = None - - c_is_resource = library.IsPixelWand - c_destroy_resource = library.DestroyPixelWand - c_get_exception = library.PixelGetException - c_clear_exception = library.PixelClearException - - __slots__ = 'raw', 'c_resource', 'allocated' - - def __init__(self, string=None, raw=None): - if (string is None and raw is None or - string is not None and raw is not None): - raise TypeError('expected one argument') - - # MagickPixelPacket has been deprecated, use PixelInfo - self.use_pixel = MAGICK_VERSION_NUMBER >= 0x700 - self.dirty = False - self.allocated = 0 - if raw is None: - if self.use_pixel: # pragma: no cover - self.raw = ctypes.create_string_buffer( - ctypes.sizeof(PixelInfo) - ) - else: - self.raw = ctypes.create_string_buffer( - ctypes.sizeof(MagickPixelPacket) - ) - with self: - # Create color from string. - ok = library.PixelSetColor(self.resource, binary(string)) - if not ok: - # Could not understand color-input. Try sending - # ImageMagick's exception. - self.raise_exception() - # That might be only a warning. Try a more generic message. - msg = 'Unrecognized color string "{0}"'.format(string) - raise ValueError(msg) - # Copy color value to structure buffer for future read. - library.PixelGetMagickColor(self.resource, self.raw) - else: - self.raw = raw - - def __getinitargs__(self): - return self.string, None - - def __enter__(self): - if self.allocated < 1: - with self.allocate(): - # Initialize resource. - self.resource = library.NewPixelWand() - # Restore color value from structure buffer. - if self.use_pixel: # pragma: no cover - library.PixelSetPixelColor(self.resource, self.raw) - else: - library.PixelSetMagickColor(self.resource, self.raw) - self.allocated = 1 - else: - self.allocated += 1 - return Resource.__enter__(self) - - def __exit__(self, type, value, traceback): - self.allocated -= 1 - if self.dirty: - library.PixelGetMagickColor(self.resource, self.raw) - self.dirty = False - if self.allocated < 1: - Resource.__exit__(self, type, value, traceback) - - def __eq__(self, other): - if not isinstance(other, Color): - return False - with self as this: - with other: - return self.c_equals(this.resource, other.resource) - - def __ne__(self, other): - return not (self == other) - - def __hash__(self): - if self.alpha: - return hash(self.normalized_string) - return hash(None) - - def __str__(self): - return self.string - - def __repr__(self): - c = type(self) - return '{0}.{1}({2!r})'.format(c.__module__, c.__name__, self.string) - - def _assert_double(self, subject): - """Ensure the given ``subject`` is a float type, and value between - 0.0 & 1.0. - - :param subject: value to assert as a valid double. - :type subject: :class:`numbers.Real` - :raises ValueError: if the subject is not between 0.0 and 1.0 - :raises TypeError: if the subject is not a float-point number. - - ..versionadded:: 0.5.1 - """ - if not isinstance(subject, numbers.Real): - raise TypeError('Expecting a float-point real number, not ' + - repr(subject)) - if subject < 0.0 or subject > 1.0: - raise ValueError('Expecting a real number between 0.0 & 1.0, not' + - repr(subject)) - - def _assert_int8(self, subject): - """Ensure the given ``subject`` is a integer type, and value between - 0 & 255. - - :param subject: value to assert as a valid number. - :type subject: :class:`numbers.Integral` - :raises ValueError: if the subject is not between 0 and 255 - :raises TypeError: if the subject is not a Integral number. - - ..versionadded:: 0.5.1 - """ - if not isinstance(subject, numbers.Integral): - raise TypeError('Expecting an integer number, not ' + - repr(subject)) - if subject < 0 or subject > 255: - raise ValueError('Expecting a real number between 0 & 255, not' + - repr(subject)) - - def _assert_quantum(self, subject): - """Ensure the given ``subject`` is a number, and value between - 0.0 & QuantumRange. - - The QuantumRange is the max value based on the QuantumDepth of the - ImageMagick library (i.e. Q16). - - :param subject: value to assert as a valid double. - :type subject: :class:`numbers.Number` - :raises ValueError: if the subject is not between 0 and QuantumRange - :raises TypeError: if the subject is not a number. - - ..versionadded:: 0.5.1 - """ - quantum_range = { - 8: 255.0, - 16: 65535.0, - 32: 4294967295.0, - 64: 18446744073709551615.0 - } - if not isinstance(subject, numbers.Number): - raise TypeError('Expecting a number, not ' + repr(subject)) - if subject < 0.0 or subject > quantum_range[QUANTUM_DEPTH]: - message = 'Expecting a number between 0 & {0}, not {1}' - raise ValueError(message.format(quantum_range[QUANTUM_DEPTH], - repr(subject))) - - def _repr_html_(self): - html = """ -   - #{red:02X}{green:02X}{blue:02X} - """ - return html.format(red=self.red_int8, - green=self.green_int8, - blue=self.blue_int8) - - @staticmethod - def c_equals(a, b): - """Raw level version of equality test function for two pixels. - - :param a: a pointer to PixelWand to compare - :type a: :class:`ctypes.c_void_p` - :param b: a pointer to PixelWand to compare - :type b: :class:`ctypes.c_void_p` - :returns: ``True`` only if two pixels equal - :rtype: :class:`bool` - - .. note:: - - It's only for internal use. Don't use it directly. - Use ``==`` operator of :class:`Color` instead. - - """ - alpha = library.PixelGetAlpha - return bool(library.IsPixelWandSimilar(a, b, 0) and - alpha(a) == alpha(b)) - - @classmethod - def from_hsl(cls, hue=0.0, saturation=0.0, lightness=0.0): - """Creates a RGB color from HSL values. The ``hue``, ``saturation``, - and ``lightness`` must be normalized between 0.0 & 1.0. - - .. code:: - - h=0.75 # 270 Degrees - s=1.0 # 100 Percent - l=0.5 # 50 Percent - with Color.from_hsl(hue=h, saturation=s, lightness=l) as color: - print(color) #=> srgb(128,0,255) - - :param hue: a normalized double between 0.0 & 1.0. - :type hue: :class:`numbers.Real` - :param saturation: a normalized double between 0.0 & 1.0. - :type saturation: :class:`numbers.Real` - :param lightness: a normalized double between 0.0 & 1.0. - :type lightness: :class:`numbers.Real` - :rtype: :class:`Color` - - .. versionadded:: 0.5.1 - """ - color = cls('WHITE') - color._assert_double(hue) - color._assert_double(saturation) - color._assert_double(lightness) - color.dirty = True - with color: - library.PixelSetHSL(color.resource, hue, saturation, lightness) - return color - - @classmethod - def from_pixelwand(cls, pixelwand): - assert pixelwand - if MAGICK_VERSION_NUMBER < 0x700: - pixel_structure = MagickPixelPacket - else: # pragma: no cover - pixel_structure = PixelInfo - size = ctypes.sizeof(pixel_structure) - raw_buffer = ctypes.create_string_buffer(size) - library.PixelGetMagickColor(pixelwand, raw_buffer) - return cls(raw=raw_buffer) - - @property - def alpha(self): - """(:class:`numbers.Real`) Alpha value, from 0.0 to 1.0.""" - with self: - return library.PixelGetAlpha(self.resource) - - @alpha.setter - def alpha(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetAlpha(self.resource, value) - - @property - def alpha_int8(self): - """(:class:`numbers.Integral`) Alpha value as 8bit integer which is - a common style. From 0 to 255. - - .. versionadded:: 0.3.0 - - """ - return max(0, min(255, int(255.0 * self.alpha))) - - @alpha_int8.setter - def alpha_int8(self, value): - self._assert_int8(value) - self.alpha = float(value) / 255.0 - - @property - def alpha_quantum(self): - """(:class:`numbers.Integral`) Alpha value. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.3.0 - - """ - with self: - return library.PixelGetAlphaQuantum(self.resource) - - @alpha_quantum.setter - def alpha_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetAlphaQuantum(self.resource, value) - - @property - def black(self): - """(:class:`numbers.Real`) Black, or ``'K'``, color channel in CMYK - colorspace. Unused by RGB colorspace. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetBlack(self.resource) - - @black.setter - def black(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetBlack(self.resource, value) - - @property - def black_int8(self): - """(:class:`numbers.Integral`) Black value as 8bit integer which is - a common style. From 0 to 255. - - .. versionadded:: 0.5.1 - """ - return max(0, min(255, int(255.0 * self.black))) - - @black_int8.setter - def black_int8(self, value): - self._assert_int8(value) - self.black = float(value) / 255.0 - - @property - def black_quantum(self): - """(:class:`numbers.Integral`) Black. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetBlackQuantum(self.resource) - - @black_quantum.setter - def black_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetBlackQuantum(self.resource, value) - - @property - def blue(self): - """(:class:`numbers.Real`) Blue, from 0.0 to 1.0.""" - with self: - return library.PixelGetBlue(self.resource) - - @blue.setter - def blue(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetBlue(self.resource, value) - - @property - def blue_int8(self): - """(:class:`numbers.Integral`) Blue as 8bit integer which is - a common style. From 0 to 255. - - .. versionadded:: 0.3.0 - - """ - return max(0, min(255, int(255.0 * self.blue))) - - @blue_int8.setter - def blue_int8(self, value): - self._assert_int8(value) - self.blue = float(value) / 255.0 - - @property - def blue_quantum(self): - """(:class:`numbers.Integral`) Blue. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.3.0 - - """ - with self: - return library.PixelGetBlueQuantum(self.resource) - - @blue_quantum.setter - def blue_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetBlueQuantum(self.resource, value) - - @property - def cyan(self): - """(:class:`numbers.Real`) Cyan color channel in CMYK - colorspace. Unused by RGB colorspace. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetCyan(self.resource) - - @cyan.setter - def cyan(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetCyan(self.resource, value) - - @property - def cyan_int8(self): - """(:class:`numbers.Integral`) Cyan value as 8bit integer which is - a common style. From 0 to 255. - - .. versionadded:: 0.5.1 - """ - return max(0, min(255, int(255.0 * self.cyan))) - - @cyan_int8.setter - def cyan_int8(self, value): - self._assert_int8(value) - self.cyan = float(value) / 255.0 - - @property - def cyan_quantum(self): - """(:class:`numbers.Integral`) Cyan. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetCyanQuantum(self.resource) - - @cyan_quantum.setter - def cyan_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetCyanQuantum(self.resource, value) - - @property - def fuzz(self): - with self: - return library.PixelGetFuzz(self.resource) - - @fuzz.setter - def fuzz(self, value): - if not isinstance(value, numbers.Real): - raise TypeError('Expecting a float-point real number, not ' + - repr(value)) - self.dirty = True - with self: - library.PixelSetFuzz(self.resource, value) - - @property - def green(self): - """(:class:`numbers.Real`) Green, from 0.0 to 1.0.""" - with self: - return library.PixelGetGreen(self.resource) - - @green.setter - def green(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetGreen(self.resource, value) - - @property - def green_int8(self): - """(:class:`numbers.Integral`) Green as 8bit integer which is - a common style. From 0 to 255. - - .. versionadded:: 0.3.0 - - """ - return max(0, min(255, int(255.0 * self.green))) - - @green_int8.setter - def green_int8(self, value): - self._assert_int8(value) - self.green = float(value) / 255.0 - - @property - def green_quantum(self): - """(:class:`numbers.Integral`) Green. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.3.0 - - """ - with self: - return library.PixelGetGreenQuantum(self.resource) - - @green_quantum.setter - def green_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetGreenQuantum(self.resource, value) - - @property - def magenta(self): - """(:class:`numbers.Real`) Magenta color channel in CMYK - colorspace. Unused by RGB colorspace. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetMagenta(self.resource) - - @magenta.setter - def magenta(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetMagenta(self.resource, value) - - @property - def magenta_int8(self): - """(:class:`numbers.Integral`) Magenta value as 8bit integer which is - a common style. From 0 to 255. - - .. versionadded:: 0.5.1 - """ - return max(0, min(255, int(255.0 * self.magenta))) - - @magenta_int8.setter - def magenta_int8(self, value): - self._assert_int8(value) - self.magenta = float(value) / 255.0 - - @property - def magenta_quantum(self): - with self: - return library.PixelGetMagentaQuantum(self.resource) - - @magenta_quantum.setter - def magenta_quantum(self, value): - """(:class:`numbers.Integral`) Magenta. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.5.1 - """ - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetMagentaQuantum(self.resource, value) - - @property - def normalized_string(self): - """(:class:`basestring`) The normalized string representation of - the color. The same color is always represented to the same - string. - - .. versionadded:: 0.3.0 - - """ - with self: - string = None - ptr = library.PixelGetColorAsNormalizedString(self.resource) - if ptr: - string = text(ctypes.string_at(ptr)) - ptr = library.MagickRelinquishMemory(ptr) - return string - - @property - def red(self): - """(:class:`numbers.Real`) Red, from 0.0 to 1.0.""" - with self: - return library.PixelGetRed(self.resource) - - @red.setter - def red(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetRed(self.resource, value) - - @property - def red_int8(self): - """(:class:`numbers.Integral`) Red as 8bit integer which is a common - style. From 0 to 255. - - .. versionadded:: 0.3.0 - - """ - return max(0, min(255, int(255.0 * self.red))) - - @red_int8.setter - def red_int8(self, value): - self._assert_int8(value) - self.red = float(value) / 255.0 - - @property - def red_quantum(self): - """(:class:`numbers.Integral`) Red. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.3.0 - - """ - with self: - return library.PixelGetRedQuantum(self.resource) - - @red_quantum.setter - def red_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetRedQuantum(self.resource, value) - - @property - def string(self): - """(:class:`basestring`) The string representation of the color.""" - with self: - color_string = None - ptr = library.PixelGetColorAsString(self.resource) - if ptr: - color_string = text(ctypes.string_at(ptr)) - ptr = library.MagickRelinquishMemory(ptr) - return color_string - - @property - def yellow(self): - """(:class:`numbers.Real`) Yellow color channel in CMYK - colorspace. Unused by RGB colorspace. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetYellow(self.resource) - - @yellow.setter - def yellow(self, value): - self._assert_double(value) - self.dirty = True - with self: - library.PixelSetYellow(self.resource, value) - - @property - def yellow_int8(self): - """(:class:`numbers.Integral`) Yellow as 8bit integer which is a common - style. From 0 to 255. - - .. versionadded:: 0.5.1 - """ - return max(0, min(255, int(255.0 * self.yellow))) - - @yellow_int8.setter - def yellow_int8(self, value): - self._assert_int8(value) - self.yellow = float(value) / 255.0 - - @property - def yellow_quantum(self): - """(:class:`numbers.Integral`) Yellow. - Scale depends on :const:`~wand.version.QUANTUM_DEPTH`. - - .. versionadded:: 0.5.1 - """ - with self: - return library.PixelGetYellowQuantum(self.resource) - - @yellow_quantum.setter - def yellow_quantum(self, value): - self._assert_quantum(value) - self.dirty = True - with self: - library.PixelSetYellowQuantum(self.resource, value) - - def hsl(self): - """Calculate the HSL color values from the RGB color. - - :returns: Tuple containing three normalized doubles, between 0.0 & - 1.0, representing ``hue``, ``saturation``, and ``lightness``. - :rtype: :class:`collections.Sequence` - - .. versionadded:: 0.5.1 - """ - hue = ctypes.c_double(0.0) - saturation = ctypes.c_double(0.0) - lightness = ctypes.c_double(0.0) - with self: - library.PixelGetHSL(self.resource, - ctypes.byref(hue), - ctypes.byref(saturation), - ctypes.byref(lightness)) - return (hue.value, saturation.value, lightness.value) - - -def scale_quantum_to_int8(quantum): - """Straightforward port of :c:func:`ScaleQuantumToChar()` inline - function. - - .. deprecated:: 0.6.6 - - :param quantum: quantum value - :type quantum: :class:`numbers.Integral` - :returns: 8bit integer of the given ``quantum`` value - :rtype: :class:`numbers.Integral` - - .. versionadded:: 0.3.0 - .. versionchanged:: 0.5.0 - Added HDRI support - """ - if quantum <= 0: - return 0 - table = {8: 1, 16: 257.0, 32: 16843009.0, 64: 72340172838076673.0} - if MAGICK_HDRI: # pragma: no cover - if QUANTUM_DEPTH == 8: - v = quantum / table[QUANTUM_DEPTH] - elif QUANTUM_DEPTH == 16: - v = ((int(quantum + 128) - (int(quantum + 128) >> 8)) >> 8) - elif QUANTUM_DEPTH == 32: - v = ((quantum + 8421504) / table[QUANTUM_DEPTH]) - elif QUANTUM_DEPTH == 64: - v = quantum / table[QUANTUM_DEPTH] - else: - v = quantum / table[QUANTUM_DEPTH] - if v >= 255: - return 255 - return int(v + 0.5) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/compat.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/compat.py deleted file mode 100644 index 5ea61ea..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/compat.py +++ /dev/null @@ -1,147 +0,0 @@ -""":mod:`wand.compat` --- Compatibility layer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This module provides several subtle things to support -multiple Python versions (2.7, 3.3+) and VM implementations -(CPython, PyPy). - -""" -import collections -try: - import collections.abc -except ImportError: - pass -import contextlib -import io -import sys -import types - -__all__ = ('PY3', 'abc', 'binary', 'binary_type', 'encode_filename', - 'file_types', 'nested', 'string_type', 'text', 'text_type', - 'to_bytes', 'xrange') - - -#: (:class:`bool`) Whether it is Python 3.x or not. -PY3 = sys.version_info >= (3,) - -#: (:class:`module`) Module containing abstract base classes. -#: :mod:`collections` in Python 2 and :mod:`collections.abc` in Python 3. -abc = collections.abc if PY3 else collections - -#: (:class:`type`) Type for representing binary data. :class:`str` in Python 2 -#: and :class:`bytes` in Python 3. -binary_type = bytes if PY3 else str - -#: (:class:`type`) Type for text data. :class:`basestring` in Python 2 -#: and :class:`str` in Python 3. -string_type = str if PY3 else basestring # noqa - -#: (:class:`type`) Type for representing Unicode textual data. -#: :class:`unicode` in Python 2 and :class:`str` in Python 3. -text_type = str if PY3 else unicode # noqa - - -def binary(string, var=None): - """Makes ``string`` to :class:`str` in Python 2. - Makes ``string`` to :class:`bytes` in Python 3. - - :param string: a string to cast it to :data:`binary_type` - :type string: :class:`bytes`, :class:`str`, :class:`unicode` - :param var: an optional variable name to be used for error message - :type var: :class:`str` - - """ - if isinstance(string, text_type): - return string.encode() - elif isinstance(string, binary_type): - return string - if var: - raise TypeError('{0} must be a string, not {1!r}'.format(var, string)) - raise TypeError('expected a string, not ' + repr(string)) - - -def to_bytes(value, string_pattern='{0}'): - """Short-cut method to allow mixed value types to be converted to bytes. - - :param value: Value to be cast to bytes - :type value: :class:`basestring`, :class:`int`, :class:`float` - :param string_pattern: String format to allow printf style control of - bytes output. - :type string_pattern: :class:`basestring` - - .. versionadded:: 0.6.4 - """ - return string_pattern.format(value).encode() - - -if PY3: - def text(string): - if isinstance(string, bytes): - return string.decode('utf-8') - return string -else: - def text(string): - """Makes ``string`` to :class:`str` in Python 3. - Does nothing in Python 2. - - :param string: a string to cast it to :data:`text_type` - :type string: :class:`bytes`, :class:`str`, :class:`unicode` - - """ - return string - - -#: The :func:`xrange()` function. Alias for :func:`range()` in Python 3. -xrange = range if PY3 else xrange # noqa - - -#: (:class:`type`, :class:`tuple`) Types for file objects that have -#: ``fileno()``. -file_types = io.RawIOBase if PY3 else (io.RawIOBase, types.FileType) - - -def encode_filename(filename): - """If ``filename`` is a :data:`text_type`, encode it to - :data:`binary_type` according to filesystem's default encoding. - - .. versionchanged:: 0.5.3 - Added support for PEP-519 https://github.com/emcconville/wand/pull/339 - """ - if hasattr(filename, "__fspath__"): # PEP 519 - filename = filename.__fspath__() - if isinstance(filename, text_type): - return filename.encode(sys.getfilesystemencoding()) - return filename - - -try: - nested = contextlib.nested -except AttributeError: - # http://hg.python.org/cpython/file/v2.7.6/Lib/contextlib.py#l88 - @contextlib.contextmanager - def nested(*managers): - exits = [] - vars = [] - exc = (None, None, None) - try: - for mgr in managers: - exit = mgr.__exit__ - enter = mgr.__enter__ - vars.append(enter()) - exits.append(exit) - yield vars - except: # noqa: E722 - exc = sys.exc_info() - finally: - while exits: - exit = exits.pop() - try: - if exit(*exc): - exc = (None, None, None) - except: # noqa: E722 - exc = sys.exc_info() - if exc != (None, None, None): - # PEP 3109 - e = exc[0](exc[1]) - e.__traceback__ = e[2] - raise e diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/display.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/display.py deleted file mode 100644 index 9ea76b3..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/display.py +++ /dev/null @@ -1,81 +0,0 @@ -""":mod:`wand.display` --- Displaying images -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :func:`display()` functions shows you the image. It is useful for -debugging. - -If you are in Mac, the image will be opened by your default image application -(:program:`Preview.app` usually). - -If you are in Windows, the image will be opened by :program:`imdisplay.exe`, -or your default image application (:program:`Windows Photo Viewer` usually) -if :program:`imdisplay.exe` is unavailable. - -You can use it from CLI also. Execute :mod:`wand.display` module through -:option:`python -m <-m>` option: - -.. sourcecode:: console - - $ python -m wand.display wandtests/assets/mona-lisa.jpg - -.. versionadded:: 0.1.9 - -""" -from __future__ import print_function -import ctypes -import os -import platform -import sys -import tempfile - -from .image import Image -from .api import library -from .exceptions import BlobError, DelegateError - -__all__ = 'display', - - -def display(image, server_name=':0'): - """Displays the passed ``image``. - - :param image: an image to display - :type image: :class:`~wand.image.Image` - :param server_name: X11 server name to use. it is ignored and not used - for Mac. default is ``':0'`` - :type server_name: :class:`str` - - """ - if not isinstance(image, Image): - raise TypeError('image must be a wand.image.Image instance, not ' + - repr(image)) - system = platform.system() - if system == 'Windows': - try: - image.save(filename='win:.') - except DelegateError: - pass - else: - return - if system in ('Windows', 'Darwin'): - ext = '.' + image.format.lower() - if ext in ('miff', 'xc'): - ext = 'png' - path = tempfile.mktemp(suffix=ext) - image.save(filename=path) - os.system(('start ' if system == 'Windows' else 'open ') + path) - else: - library.MagickDisplayImage.argtypes = [ctypes.c_void_p, - ctypes.c_char_p] - library.MagickDisplayImage(image.wand, str(server_name).encode()) - - -if __name__ == '__main__': - if len(sys.argv) < 2: - print('usage: python -m wand.display FILE', file=sys.stderr) - raise SystemExit - path = sys.argv[1] - try: - with Image(filename=path) as image: - display(image) - except BlobError: - print('cannot read the file', path, file=sys.stderr) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/drawing.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/drawing.py deleted file mode 100644 index 652916c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/drawing.py +++ /dev/null @@ -1,2131 +0,0 @@ -""":mod:`wand.drawing` --- Drawings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The module provides some vector drawing functions. - -.. versionadded:: 0.3.0 - -""" -import collections -import ctypes -import numbers - -from . import assertions -from .cdefs.structures import AffineMatrix, PointInfo -from .api import library -from .color import Color -from .compat import abc, binary, string_type, text, text_type, xrange -from .exceptions import WandLibraryVersionError -from .image import BaseImage, COMPOSITE_OPERATORS -from .sequence import SingleImage -from .resource import Resource - -__all__ = ('CLIP_PATH_UNITS', 'FILL_RULE_TYPES', 'FONT_METRICS_ATTRIBUTES', - 'GRAVITY_TYPES', 'LINE_CAP_TYPES', 'LINE_JOIN_TYPES', - 'PAINT_METHOD_TYPES', 'STRETCH_TYPES', 'STYLE_TYPES', - 'TEXT_ALIGN_TYPES', 'TEXT_DECORATION_TYPES', - 'TEXT_DIRECTION_TYPES', 'Drawing', 'FontMetrics') - - -#: (:class:`collections.abc.Sequence`) The list of clip path units -#: -#: - ``'undefined_path_units'`` -#: - ``'user_space'`` -#: - ``'user_space_on_use'`` -#: - ``'object_bounding_box'`` -CLIP_PATH_UNITS = ('undefined_path_units', 'user_space', 'user_space_on_use', - 'object_bounding_box') - -#: (:class:`collections.abc.Sequence`) The list of text align types. -#: -#: - ``'undefined'`` -#: - ``'left'`` -#: - ``'center'`` -#: - ``'right'`` -TEXT_ALIGN_TYPES = 'undefined', 'left', 'center', 'right' - -#: (:class:`collections.abc.Sequence`) The list of text decoration types. -#: -#: - ``'undefined'`` -#: - ``'no'`` -#: - ``'underline'`` -#: - ``'overline'`` -#: - ``'line_through'`` -TEXT_DECORATION_TYPES = ('undefined', 'no', 'underline', 'overline', - 'line_through') - -#: (:class:`collections.abc.Sequence`) The list of text direction types. -#: -#: - ``'undefined'`` -#: - ``'right_to_left'`` -#: - ``'left_to_right'`` -TEXT_DIRECTION_TYPES = ('undefined', 'right_to_left', 'left_to_right') - -#: (:class:`collections.abc.Sequence`) The list of text gravity types. -#: -#: - ``'forget'`` -#: - ``'north_west'`` -#: - ``'north'`` -#: - ``'north_east'`` -#: - ``'west'`` -#: - ``'center'`` -#: - ``'east'`` -#: - ``'south_west'`` -#: - ``'south'`` -#: - ``'south_east'`` -#: - ``'static'`` -GRAVITY_TYPES = ('forget', 'north_west', 'north', 'north_east', 'west', - 'center', 'east', 'south_west', 'south', 'south_east', - 'static') - -#: (:class:`collections.abc.Sequence`) The list of fill-rule types. -#: -#: - ``'undefined'`` -#: - ``'evenodd'`` -#: - ``'nonzero'`` -FILL_RULE_TYPES = ('undefined', 'evenodd', 'nonzero') - -#: (:class:`collections.abc.Sequence`) The attribute names of font metrics. -FONT_METRICS_ATTRIBUTES = ('character_width', 'character_height', 'ascender', - 'descender', 'text_width', 'text_height', - 'maximum_horizontal_advance', 'x1', 'y1', 'x2', - 'y2', 'x', 'y') - -#: (:class:`collections.abc.Sequence`) The list of stretch types for fonts -#: -#: - ``'undefined;`` -#: - ``'normal'`` -#: - ``'ultra_condensed'`` -#: - ``'extra_condensed'`` -#: - ``'condensed'`` -#: - ``'semi_condensed'`` -#: - ``'semi_expanded'`` -#: - ``'expanded'`` -#: - ``'extra_expanded'`` -#: - ``'ultra_expanded'`` -#: - ``'any'`` -STRETCH_TYPES = ('undefined', 'normal', 'ultra_condensed', 'extra_condensed', - 'condensed', 'semi_condensed', 'semi_expanded', 'expanded', - 'extra_expanded', 'ultra_expanded', 'any') - -#: (:class:`collections.abc.Sequence`) The list of style types for fonts -#: -#: - ``'undefined;`` -#: - ``'normal'`` -#: - ``'italic'`` -#: - ``'oblique'`` -#: - ``'any'`` -STYLE_TYPES = ('undefined', 'normal', 'italic', 'oblique', 'any') - -#: (:class:`collections.abc.Sequence`) The list of LineCap types -#: -#: - ``'undefined;`` -#: - ``'butt'`` -#: - ``'round'`` -#: - ``'square'`` -LINE_CAP_TYPES = ('undefined', 'butt', 'round', 'square') - -#: (:class:`collections.abc.Sequence`) The list of LineJoin types -#: -#: - ``'undefined'`` -#: - ``'miter'`` -#: - ``'round'`` -#: - ``'bevel'`` -LINE_JOIN_TYPES = ('undefined', 'miter', 'round', 'bevel') - - -#: (:class:`collections.abc.Sequence`) The list of paint method types. -#: -#: - ``'undefined'`` -#: - ``'point'`` -#: - ``'replace'`` -#: - ``'floodfill'`` -#: - ``'filltoborder'`` -#: - ``'reset'`` -PAINT_METHOD_TYPES = ('undefined', 'point', 'replace', - 'floodfill', 'filltoborder', 'reset') - - -class Drawing(Resource): - """Drawing object. It maintains several vector drawing instructions - and can get drawn into zero or more :class:`~wand.image.Image` objects - by calling it. - - For example, the following code draws a diagonal line to the ``image``:: - - with Drawing() as draw: - draw.line((0, 0), image.size) - draw(image) - - :param drawing: an optional drawing object to clone. - use :meth:`clone()` method rather than this parameter - :type drawing: :class:`Drawing` - - .. versionadded:: 0.3.0 - - """ - - c_is_resource = library.IsDrawingWand - c_destroy_resource = library.DestroyDrawingWand - c_get_exception = library.DrawGetException - c_clear_exception = library.DrawClearException - - def __init__(self, drawing=None): - with self.allocate(): - if not drawing: - wand = library.NewDrawingWand() - elif not isinstance(drawing, type(self)): - raise TypeError('drawing must be a wand.drawing.Drawing ' - 'instance, not ' + repr(drawing)) - else: - wand = library.CloneDrawingWand(drawing.resource) - self.resource = wand - - @property - def border_color(self): - """(:class:`~wand.color.Color`) the current border color. It also can - be set. This attribute controls the behavior of - :meth:`~wand.drawing.Drawing.color()` during ``'filltoborder'`` - operation. - - .. versionadded:: 0.4.0 - """ - pixelwand = library.NewPixelWand() - library.DrawGetBorderColor(self.resource, pixelwand) - color = Color.from_pixelwand(pixelwand) - pixelwand = library.DestroyPixelWand(pixelwand) - return color - - @border_color.setter - def border_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(border_color=color) - with color: - library.DrawSetBorderColor(self.resource, color.resource) - - @property - def clip_path(self): - """(:class:`basestring`) The current clip path. It also can be set. - - .. versionadded:: 0.4.0 - - .. versionchanged: 0.4.1 - Safely release allocated memory with - :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. - - """ - clip_path_str = None - clip_path_p = library.DrawGetClipPath(self.resource) - if clip_path_p: - clip_path_str = text(ctypes.string_at(clip_path_p)) - clip_path_p = library.MagickRelinquishMemory(clip_path_p) - return clip_path_str - - @clip_path.setter - def clip_path(self, path): - assertions.assert_string(clip_path=path) - library.DrawSetClipPath(self.resource, binary(path)) - - @property - def clip_rule(self): - """(:class:`basestring`) The current clip rule. It also can be set. - It's a string value from :const:`FILL_RULE_TYPES` list. - - .. versionadded:: 0.4.0 - """ - clip_rule = library.DrawGetClipRule(self.resource) - return FILL_RULE_TYPES[clip_rule] - - @clip_rule.setter - def clip_rule(self, clip_rule): - assertions.string_in_list(FILL_RULE_TYPES, - 'wand.drawing.FILL_RULE_TYPES', - clip_rule=clip_rule) - library.DrawSetClipRule(self.resource, - FILL_RULE_TYPES.index(clip_rule)) - - @property - def clip_units(self): - """(:class:`basestring`) The current clip units. It also can be set. - It's a string value from :const:`CLIP_PATH_UNITS` list. - - .. versionadded:: 0.4.0 - """ - clip_unit = library.DrawGetClipUnits(self.resource) - return CLIP_PATH_UNITS[clip_unit] - - @clip_units.setter - def clip_units(self, clip_unit): - assertions.string_in_list(CLIP_PATH_UNITS, - 'wand.drawing.CLIP_PATH_UNITS', - clip_unit=clip_unit) - library.DrawSetClipUnits(self.resource, - CLIP_PATH_UNITS.index(clip_unit)) - - @property - def fill_color(self): - """(:class:`~wand.color.Color`) The current color to fill. - It also can be set. - - """ - pixel = library.NewPixelWand() - library.DrawGetFillColor(self.resource, pixel) - color = Color.from_pixelwand(pixel) - pixel = library.DestroyPixelWand(pixel) - return color - - @fill_color.setter - def fill_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(fill_color=color) - with color: - library.DrawSetFillColor(self.resource, color.resource) - - @property - def fill_opacity(self): - """(:class:`~numbers.Real`) The current fill opacity. - It also can be set. - - .. versionadded:: 0.4.0 - """ - return library.DrawGetFillOpacity(self.resource) - - @fill_opacity.setter - def fill_opacity(self, opacity): - assertions.assert_real(fill_opacity=opacity) - library.DrawSetFillOpacity(self.resource, opacity) - - @property - def fill_rule(self): - """(:class:`basestring`) The current fill rule. It can also be set. - It's a string value from :const:`FILL_RULE_TYPES` list. - - .. versionadded:: 0.4.0 - """ - fill_rule_index = library.DrawGetFillRule(self.resource) - if fill_rule_index not in FILL_RULE_TYPES: - self.raise_exception() - return text(FILL_RULE_TYPES[fill_rule_index]) - - @fill_rule.setter - def fill_rule(self, fill_rule): - assertions.string_in_list(FILL_RULE_TYPES, - 'wand.drawing.FILL_RULE_TYPES', - fill_rule=fill_rule) - library.DrawSetFillRule(self.resource, - FILL_RULE_TYPES.index(fill_rule)) - - @property - def font(self): - """(:class:`basestring`) The current font name. It also can be set. - - .. versionchanged: 0.4.1 - Safely release allocated memory with - :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. - - """ - font_str = None - font_p = library.DrawGetFont(self.resource) - if font_p: - font_str = text(ctypes.string_at(font_p)) - font_p = library.MagickRelinquishMemory(font_p) - return font_str - - @font.setter - def font(self, font): - assertions.assert_string(font=font) - library.DrawSetFont(self.resource, binary(font)) - - @property - def font_family(self): - """(:class:`basestring`) The current font family. It also can be set. - - .. versionadded:: 0.4.0 - - .. versionchanged: 0.4.1 - Safely release allocated memory with - :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. - - """ - font_family_str = None - font_family_p = library.DrawGetFontFamily(self.resource) - if font_family_p: - font_family_str = text(ctypes.string_at(font_family_p)) - font_family_p = library.MagickRelinquishMemory(font_family_p) - return font_family_str - - @font_family.setter - def font_family(self, family): - assertions.assert_string(font_family=family) - library.DrawSetFontFamily(self.resource, binary(family)) - - @property - def font_resolution(self): - """(:class:`~collections.abc.Sequence`) The current font resolution. - It also can be set. - - .. versionadded:: 0.4.0 - """ - x, y = ctypes.c_double(0.0), ctypes.c_double(0.0) - library.DrawGetFontResolution(self.resource, - ctypes.byref(x), - ctypes.byref(y)) - return x.value, y.value - - @font_resolution.setter - def font_resolution(self, resolution): - assertions.assert_coordinate(font_resolution=resolution) - library.DrawSetFontResolution(self.resource, *resolution) - - @property - def font_size(self): - """(:class:`numbers.Real`) The font size. It also can be set.""" - return library.DrawGetFontSize(self.resource) - - @font_size.setter - def font_size(self, size): - assertions.assert_real(font_size=size) - if size < 0.0: - raise ValueError('cannot be less than 0.0, but got ' + repr(size)) - library.DrawSetFontSize(self.resource, size) - - @property - def font_stretch(self): - """(:class:`basestring`) The current font stretch variation. - It also can be set, but will only apply if the font-family or encoder - supports the stretch type. - - .. versionadded:: 0.4.0 - """ - stretch_index = library.DrawGetFontStretch(self.resource) - return text(STRETCH_TYPES[stretch_index]) - - @font_stretch.setter - def font_stretch(self, stretch): - assertions.string_in_list(STRETCH_TYPES, - 'wand.drawing.STRETCH_TYPES', - font_stretch=stretch) - library.DrawSetFontStretch(self.resource, - STRETCH_TYPES.index(stretch)) - - @property - def font_style(self): - """(:class:`basestring`) The current font style. - It also can be set, but will only apply if the font-family - supports the style. - - .. versionadded:: 0.4.0 - """ - style_index = library.DrawGetFontStyle(self.resource) - return text(STYLE_TYPES[style_index]) - - @font_style.setter - def font_style(self, style): - assertions.string_in_list(STYLE_TYPES, - 'wand.drawing.STYLE_TYPES', - font_style=style) - library.DrawSetFontStyle(self.resource, - STYLE_TYPES.index(style)) - - @property - def font_weight(self): - """(:class:`~numbers.Integral`) The current font weight. - It also can be set. - - .. versionadded:: 0.4.0 - """ - return library.DrawGetFontWeight(self.resource) - - @font_weight.setter - def font_weight(self, weight): - assertions.assert_integer(font_weight=weight) - library.DrawSetFontWeight(self.resource, weight) - - @property - def gravity(self): - """(:class:`basestring`) The text placement gravity used when - annotating with text. It's a string from :const:`GRAVITY_TYPES` - list. It also can be set. - - """ - gravity_index = library.DrawGetGravity(self.resource) - if not gravity_index: - self.raise_exception() - return text(GRAVITY_TYPES[gravity_index]) - - @gravity.setter - def gravity(self, value): - assertions.string_in_list(GRAVITY_TYPES, - 'wand.drawing.GRAVITY_TYPES', - gravity=value) - library.DrawSetGravity(self.resource, GRAVITY_TYPES.index(value)) - - @property - def opacity(self): - """(:class:`~numbers.Real`) returns the opacity used when drawing with - the fill or stroke color or texture. Fully opaque is 1.0. This method - only affects vector graphics, and is experimental. To set the opacity - of a drawing, use - :attr:`Drawing.fill_opacity` & :attr:`Drawing.stroke_opacity` - - .. versionadded:: 0.4.0 - """ - return library.DrawGetOpacity(self.resource) - - @opacity.setter - def opacity(self, opaque): - assertions.assert_real(opacity=opaque) - library.DrawSetOpacity(self.resource, opaque) - - @property - def stroke_antialias(self): - """(:class:`bool`) Controls whether stroked outlines are antialiased. - Stroked outlines are antialiased by default. When antialiasing is - disabled stroked pixels are thresholded to determine if the stroke - color or underlying canvas color should be used. - - It also can be set. - - .. versionadded:: 0.4.0 - - """ - stroke_antialias = library.DrawGetStrokeAntialias(self.resource) - return bool(stroke_antialias) - - @stroke_antialias.setter - def stroke_antialias(self, stroke_antialias): - assertions.assert_bool(stroke_antialias=stroke_antialias) - library.DrawSetStrokeAntialias(self.resource, stroke_antialias) - - @property - def stroke_color(self): - """(:class:`~wand.color.Color`) The current color of stroke. - It also can be set. - - .. versionadded:: 0.3.3 - - """ - pixel = library.NewPixelWand() - library.DrawGetStrokeColor(self.resource, pixel) - color = Color.from_pixelwand(pixel) - pixel = library.DestroyPixelWand(pixel) - return color - - @stroke_color.setter - def stroke_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(stroke_color=color) - with color: - library.DrawSetStrokeColor(self.resource, color.resource) - - @property - def stroke_dash_array(self): - """(:class:`~collections.abc.Sequence`) - (:class:`numbers.Real`) An - array representing the pattern of dashes & gaps used to stroke paths. - It also can be set. - - .. versionadded:: 0.4.0 - - .. versionchanged: 0.4.1 - Safely release allocated memory with - :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. - - """ - number_elements = ctypes.c_size_t(0) - dash_array_p = library.DrawGetStrokeDashArray( - self.resource, ctypes.byref(number_elements) - ) - dash_array = [] - if dash_array_p: - dash_array = [float(dash_array_p[i]) - for i in xrange(number_elements.value)] - dash_array_p = library.MagickRelinquishMemory(dash_array_p) - return dash_array - - @stroke_dash_array.setter - def stroke_dash_array(self, dash_array): - dash_array_l = len(dash_array) - dash_array_p = (ctypes.c_double * dash_array_l)(*dash_array) - library.DrawSetStrokeDashArray(self.resource, - dash_array_l, - dash_array_p) - - @property - def stroke_dash_offset(self): - """(:class:`numbers.Real`) The stroke dash offset. It also can be set. - - .. versionadded:: 0.4.0 - """ - return library.DrawGetStrokeDashOffset(self.resource) - - @stroke_dash_offset.setter - def stroke_dash_offset(self, offset): - assertions.assert_real(stroke_dash_offset=offset) - library.DrawSetStrokeDashOffset(self.resource, offset) - - @property - def stroke_line_cap(self): - """(:class:`basestring`) The stroke line cap. It also can be set. - - .. versionadded:: 0.4.0 - """ - line_cap_index = library.DrawGetStrokeLineCap(self.resource) - if line_cap_index not in LINE_CAP_TYPES: - self.raise_exception() - return text(LINE_CAP_TYPES[line_cap_index]) - - @stroke_line_cap.setter - def stroke_line_cap(self, line_cap): - assertions.string_in_list(LINE_CAP_TYPES, - 'wand.drawing.LINE_CAP_TYPES', - stroke_line_cap=line_cap) - library.DrawSetStrokeLineCap(self.resource, - LINE_CAP_TYPES.index(line_cap)) - - @property - def stroke_line_join(self): - """(:class:`basestring`) The stroke line join. It also can be set. - - .. versionadded:: 0.4.0 - """ - line_join_index = library.DrawGetStrokeLineJoin(self.resource) - if line_join_index not in LINE_JOIN_TYPES: - self.raise_exception() - return text(LINE_JOIN_TYPES[line_join_index]) - - @stroke_line_join.setter - def stroke_line_join(self, line_join): - assertions.string_in_list(LINE_JOIN_TYPES, - 'wand.drawing.LINE_JOIN_TYPES', - stroke_line_join=line_join) - library.DrawSetStrokeLineJoin(self.resource, - LINE_JOIN_TYPES.index(line_join)) - - @property - def stroke_miter_limit(self): - """(:class:`~numbers.Integral`) The current miter limit. - It also can be set. - - .. versionadded:: 0.4.0 - """ - return library.DrawGetStrokeMiterLimit(self.resource) - - @stroke_miter_limit.setter - def stroke_miter_limit(self, miter_limit): - assertions.assert_integer(stroke_miter_limit=miter_limit) - library.DrawSetStrokeMiterLimit(self.resource, miter_limit) - - @property - def stroke_opacity(self): - """(:class:`~numbers.Real`) The current stroke opacity. - It also can be set. - - .. versionadded:: 0.4.0 - """ - return library.DrawGetStrokeOpacity(self.resource) - - @stroke_opacity.setter - def stroke_opacity(self, opacity): - assertions.assert_real(stroke_opacity=opacity) - library.DrawSetStrokeOpacity(self.resource, opacity) - - @property - def stroke_width(self): - """(:class:`numbers.Real`) The stroke width. It also can be set. - - .. versionadded:: 0.3.3 - - """ - return library.DrawGetStrokeWidth(self.resource) - - @stroke_width.setter - def stroke_width(self, width): - assertions.assert_real(stroke_width=width) - if width < 0.0: - raise ValueError('cannot be less than 0.0, but got ' + repr(width)) - library.DrawSetStrokeWidth(self.resource, width) - - @property - def text_alignment(self): - """(:class:`basestring`) The current text alignment setting. - It's a string value from :const:`TEXT_ALIGN_TYPES` list. - It also can be set. - - """ - text_alignment_index = library.DrawGetTextAlignment(self.resource) - if not text_alignment_index: # pragma: no cover - self.raise_exception() - return text(TEXT_ALIGN_TYPES[text_alignment_index]) - - @text_alignment.setter - def text_alignment(self, align): - assertions.string_in_list(TEXT_ALIGN_TYPES, - 'wand.drawing.TEXT_ALIGN_TYPES', - text_alignment=align) - library.DrawSetTextAlignment(self.resource, - TEXT_ALIGN_TYPES.index(align)) - - @property - def text_antialias(self): - """(:class:`bool`) The boolean value which represents whether - antialiasing is used for text rendering. It also can be set to - ``True`` or ``False`` to switch the setting. - - """ - result = library.DrawGetTextAntialias(self.resource) - return bool(result) - - @text_antialias.setter - def text_antialias(self, value): - assertions.assert_bool(text_antialias=value) - library.DrawSetTextAntialias(self.resource, value) - - @property - def text_decoration(self): - """(:class:`basestring`) The text decoration setting, a string - from :const:`TEXT_DECORATION_TYPES` list. It also can be set. - - """ - text_decoration_index = library.DrawGetTextDecoration(self.resource) - if not text_decoration_index: # pragma: no cover - self.raise_exception() - return text(TEXT_DECORATION_TYPES[text_decoration_index]) - - @text_decoration.setter - def text_decoration(self, decoration): - assertions.string_in_list(TEXT_DECORATION_TYPES, - 'wand.drawing.TEXT_DECORATION_TYPES', - text_decoration=decoration) - library.DrawSetTextDecoration(self.resource, - TEXT_DECORATION_TYPES.index(decoration)) - - @property - def text_direction(self): - """(:class:`basestring`) The text direction setting. a string - from :const:`TEXT_DIRECTION_TYPES` list. It also can be set.""" - if library.DrawGetTextDirection is None: # pragma: no cover - raise WandLibraryVersionError( - 'the installed version of ImageMagick does not support ' - 'this feature' - ) - text_direction_index = library.DrawGetTextDirection(self.resource) - if not text_direction_index: # pragma: no cover - self.raise_exception() - return text(TEXT_DIRECTION_TYPES[text_direction_index]) - - @text_direction.setter - def text_direction(self, direction): - if library.DrawGetTextDirection is None: # pragma: no cover - raise WandLibraryVersionError( - 'The installed version of ImageMagick does not support ' - 'this feature' - ) - assertions.string_in_list(TEXT_DIRECTION_TYPES, - 'wand.drawing.TEXT_DIRECTION_TYPES', - text_direction=direction) - library.DrawSetTextDirection(self.resource, - TEXT_DIRECTION_TYPES.index(direction)) - - @property - def text_encoding(self): - """(:class:`basestring`) The internally used text encoding setting. - Although it also can be set, but it's not encouraged. - - .. versionchanged: 0.4.1 - Safely release allocated memory with - :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. - - """ - text_encoding_str = None - text_encoding_p = library.DrawGetTextEncoding(self.resource) - if text_encoding_p: - text_encoding_str = text(ctypes.string_at(text_encoding_p)) - text_encoding_p = library.MagickRelinquishMemory(text_encoding_p) - return text_encoding_str - - @text_encoding.setter - def text_encoding(self, encoding): - if encoding is None: - # encoding specify an empty string to set text encoding - # to system's default. - encoding = b'' - else: - assertions.assert_string(text_encoding=encoding) - encoding = binary(encoding) - library.DrawSetTextEncoding(self.resource, encoding) - - @property - def text_interline_spacing(self): - """(:class:`numbers.Real`) The setting of the text line spacing. - It also can be set. - - """ - if library.DrawGetTextInterlineSpacing is None: # pragma: no cover - raise WandLibraryVersionError('The installed version of ' - 'ImageMagick does not support ' - 'this feature') - return library.DrawGetTextInterlineSpacing(self.resource) - - @text_interline_spacing.setter - def text_interline_spacing(self, spacing): - if library.DrawSetTextInterlineSpacing is None: # pragma: no cover - raise WandLibraryVersionError('The installed version of ' - 'ImageMagick does not support ' - 'this feature') - assertions.assert_real(text_interline_spacing=spacing) - library.DrawSetTextInterlineSpacing(self.resource, spacing) - - @property - def text_interword_spacing(self): - """(:class:`numbers.Real`) The setting of the word spacing. - It also can be set. - - """ - return library.DrawGetTextInterwordSpacing(self.resource) - - @text_interword_spacing.setter - def text_interword_spacing(self, spacing): - assertions.assert_real(text_interword_spacing=spacing) - library.DrawSetTextInterwordSpacing(self.resource, spacing) - - @property - def text_kerning(self): - """(:class:`numbers.Real`) The setting of the text kerning. - It also can be set. - - """ - return library.DrawGetTextKerning(self.resource) - - @text_kerning.setter - def text_kerning(self, kerning): - assertions.assert_real(text_kerning=kerning) - library.DrawSetTextKerning(self.resource, kerning) - - @property - def text_under_color(self): - """(:class:`~wand.color.Color`) The color of a background rectangle - to place under text annotations. It also can be set. - - """ - pixel = library.NewPixelWand() - library.DrawGetTextUnderColor(self.resource, pixel) - color = Color.from_pixelwand(pixel) - pixel = library.DestroyPixelWand(pixel) - return color - - @text_under_color.setter - def text_under_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(text_under_color=color) - with color: - library.DrawSetTextUnderColor(self.resource, color.resource) - - @property - def vector_graphics(self): - """(:class:`basestring`) The XML text of the Vector Graphics. - It also can be set. The drawing-wand XML is experimental, - and subject to change. - - Setting this property to None will reset all vector graphic properties - to the default state. - - .. versionadded:: 0.4.0 - - .. versionchanged: 0.4.1 - Safely release allocated memory with - :c:func:`MagickRelinquishMemory` instead of :c:func:`libc.free`. - - """ - vg_p = library.DrawGetVectorGraphics(self.resource) - if vg_p: - vg_str = ctypes.string_at(vg_p) - vg_p = library.MagickRelinquishMemory(vg_p) - else: - vg_str = b'' - return '' + text(vg_str) + '' - - @vector_graphics.setter - def vector_graphics(self, vector_graphics): - if vector_graphics is not None and not isinstance(vector_graphics, - string_type): - raise TypeError('expected a string, not ' + repr(vector_graphics)) - elif vector_graphics is None: - # Reset all vector graphic properties on drawing wand. - library.DrawResetVectorGraphics(self.resource) - else: - vector_graphics = binary(vector_graphics) - okay = library.DrawSetVectorGraphics(self.resource, - vector_graphics) - if okay == 0: # pragma: no cover - raise ValueError("Vector graphic not understood.") - - def affine(self, matrix): - """Adjusts the current affine transformation matrix with the specified - affine transformation matrix. Note that the current affine transform is - adjusted rather than replaced. - - .. sourcecode:: text - - | sx rx 0 | - | x', y', 1 | = | x, y, 1 | * | ry sy 0 | - | tx ty 1 | - - :param matrix: a list of :class:`~numbers.Real` to define affine - matrix ``[sx, rx, ry, sy, tx, ty]`` - :type matrix: :class:`collections.abc.Sequence` - - .. versionadded:: 0.4.0 - - """ - if not isinstance(matrix, abc.Sequence) or len(matrix) != 6: - raise ValueError('matrix must be a list of size Real numbers') - for idx, val in enumerate(matrix): - if not isinstance(val, numbers.Real): - raise TypeError('expecting numbers.Real in position #' + - repr(idx)) - amx = AffineMatrix(sx=matrix[0], rx=matrix[1], - ry=matrix[2], sy=matrix[3], - tx=matrix[4], ty=matrix[5]) - library.DrawAffine(self.resource, ctypes.byref(amx)) - - def alpha(self, x=None, y=None, paint_method='undefined'): - """Paints on the image's opacity channel in order to set effected - pixels to transparent. - - To influence the opacity of pixels. The available methods are: - - - ``'undefined'`` - - ``'point'`` - - ``'replace'`` - - ``'floodfill'`` - - ``'filltoborder'`` - - ``'reset'`` - - .. note:: - - This method replaces :meth:`matte()` in ImageMagick version 7. - An :class:`AttributeError` will be raised if attempting - to call on a library without ``DrawAlpha`` support. - - .. versionadded:: 0.5.0 - - """ - if library.DrawAlpha is None: - raise AttributeError( - 'Method added with ImageMagick version 7. ' + - 'Please use `wand.drawing.Drawing.matte()\' instead.' - ) - assertions.assert_real(x=x, y=y) - assertions.string_in_list(PAINT_METHOD_TYPES, - 'wand.drawing.PAINT_METHOD_TYPES', - paint_method=paint_method) - op = PAINT_METHOD_TYPES.index(paint_method) - library.DrawAlpha(self.resource, x, y, op) - - def arc(self, start, end, degree): - """Draws a arc using the current :attr:`stroke_color`, - :attr:`stroke_width`, and :attr:`fill_color`. - - :param start: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents starting x and y of the arc - :type start: :class:`~collections.abc.Sequence` - :param end: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents ending x and y of the arc - :type end: :class:`~collections.abc.Sequence` - :param degree: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents starting degree, and ending degree - :type degree: :class:`~collections.abc.Sequence` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_coordinate(start=start, end=end, degree=degree) - start_x, start_y = start - end_x, end_y = end - degree_start, degree_end = degree - library.DrawArc(self.resource, - float(start_x), float(start_y), - float(end_x), float(end_y), - float(degree_start), float(degree_end)) - - def bezier(self, points=None): - """Draws a bezier curve through a set of points on the image, using - the specified array of coordinates. - - At least four points should be given to complete a bezier path. - The first & forth point being the start & end point, and the second - & third point controlling the direction & curve. - - Example bezier on ``image`` :: - - with Drawing() as draw: - points = [(40,10), # Start point - (20,50), # First control - (90,10), # Second control - (70,40)] # End point - draw.stroke_color = Color('#000') - draw.fill_color = Color('#fff') - draw.bezier(points) - draw.draw(image) - - :param points: list of x,y tuples - :type points: :class:`list` - - .. versionadded:: 0.4.0 - - """ - - (points_l, points_p) = _list_to_point_info(points) - library.DrawBezier(self.resource, points_l, - ctypes.cast(points_p, ctypes.POINTER(PointInfo))) - - def circle(self, origin, perimeter): - """Draws a circle from ``origin`` to ``perimeter`` - - :param origin: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents origin x and y of circle - :type origin: :class:`collections.abc.Sequence` - :param perimeter: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents perimeter x and y of circle - :type perimeter: :class:`collections.abc.Sequence` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_coordinate(origin=origin, perimeter=perimeter) - origin_x, origin_y = origin - perimeter_x, perimeter_y = perimeter - library.DrawCircle(self.resource, origin_x, origin_y, - perimeter_x, perimeter_y) - - def clear(self): - library.ClearDrawingWand(self.resource) - - def clone(self): - """Copies a drawing object. - - :returns: a duplication - :rtype: :class:`Drawing` - - """ - return type(self)(drawing=self) - - def color(self, x=0.0, y=0.0, paint_method='undefined'): - """Draws a color on the image using current fill color, starting - at specified position & method. - - Available methods in :class:`wand.drawing.PAINT_METHOD_TYPES`: - - - ``'undefined'`` - - ``'point'`` - - ``'replace'`` - - ``'floodfill'`` - - ``'filltoborder'`` - - ``'reset'`` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_real(x=x, y=y) - assertions.string_in_list(PAINT_METHOD_TYPES, - 'wand.drawing.PAINT_METHOD_TYPES', - paint_method=paint_method) - op = PAINT_METHOD_TYPES.index(paint_method) - library.DrawColor(self.resource, x, y, op) - - def comment(self, message=None): - """Adds a comment to the vector stream. - - :param message: the comment to set. - :type message: :class:`basestring` - - .. versionadded:: 0.4.0 - """ - if message is None: - message = b'' - else: - assertions.assert_string(message=message) - message = binary(message) - library.DrawComment(self.resource, message) - - def composite(self, operator, left, top, width, height, image): - """Composites an image onto the current image, using the specified - composition operator, specified position, and at the specified size. - - :param operator: the operator that affects how the composite - is applied to the image. available values - can be found in the :const:`COMPOSITE_OPERATORS` - list - :param type: :const:`COMPOSITE_OPERATORS` - :param left: the column offset of the composited drawing source - :type left: :class:`numbers.Real` - :param top: the row offset of the composited drawing source - :type top: :class:`numbers.Real` - :param width: the total columns to include in the composited source - :type width: :class:`numbers.Real` - :param height: the total rows to include in the composited source - :type height: :class:`numbers.Real` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_string(operator=operator) - assertions.assert_real(left=left, top=top, width=width, height=height) - assertions.string_in_list(COMPOSITE_OPERATORS, - 'wand.drawing.COMPOSITE_OPERATORS', - operator=operator) - op = COMPOSITE_OPERATORS.index(operator) - library.DrawComposite(self.resource, op, left, top, width, - height, image.wand) - self.raise_exception() - - def draw(self, image): - """Renders the current drawing into the ``image``. You can simply - call :class:`Drawing` instance rather than calling this method. - That means the following code which calls :class:`Drawing` object - itself:: - - drawing(image) - - is equivalent to the following code which calls :meth:`draw()` method:: - - drawing.draw(image) - - :param image: the image to be drawn - :type image: :class:`~wand.image.BaseImage` - - """ - if not isinstance(image, BaseImage): - raise TypeError('image must be a wand.image.BaseImage instance,' - ' not ' + repr(image)) - if isinstance(image, SingleImage): - previous = library.MagickGetIteratorIndex(image.container.wand) - library.MagickSetIteratorIndex(image.container.wand, image.index) - res = library.MagickDrawImage(image.container.wand, self.resource) - library.MagickSetIteratorIndex(image.container.wand, previous) - else: - res = library.MagickDrawImage(image.wand, self.resource) - if not res: - self.raise_exception() - - def ellipse(self, origin, radius, rotation=None): - """Draws a ellipse at ``origin`` with independent x & y ``radius``. - Ellipse can be partial by setting start & end ``rotation``. - - :param origin: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents origin x and y of circle - :type origin: :class:`collections.abc.Sequence` - :param radius: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents radius x and radius y of circle - :type radius: :class:`collections.abc.Sequence` - :param rotation: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents start and end of ellipse. - Default (0,360) - :type rotation: :class:`collections.abc.Sequence` - - .. versionadded:: 0.4.0 - - """ - if rotation is None: - rotation = (0, 360) - assertions.assert_coordinate(origin=origin, radius=radius, - rotation=rotation) - origin_x, origin_y = origin - radius_x, radius_y = radius - rotation_start, rotation_end = rotation - library.DrawEllipse(self.resource, - origin_x, origin_y, - radius_x, radius_y, - rotation_start, rotation_end) - - def get_font_metrics(self, image, text, multiline=False): - """Queries font metrics by cloning the ``image``, and rendering - the ``text`` with the font properties defined on :class:`Drawing`. - - This is useful for determining the size of the rendered text. Set - ``multiline`` to ``True`` if the given ``text`` contains line-breaks. - - The return value is an instance of :class:`FontMetrics` which - has the attributes of the text's rendered size, max horizontal advance, - and distance of ascent & descent from the baseline. - - :param image: the image to be drawn - :type image: :class:`~wand.image.BaseImage` - :param text: the text string for get font metrics. - :type text: :class:`basestring` - :param multiline: text is multiline or not - :type multiline: `boolean` - :returns: :class:`FontMetrics` - - .. versionadded:: 0.3.0 - """ - if not isinstance(image, BaseImage): - raise TypeError('image must be a wand.image.BaseImage instance,' - ' not ' + repr(image)) - assertions.assert_string(text=text) - if multiline: - font_metrics_f = library.MagickQueryMultilineFontMetrics - else: - font_metrics_f = library.MagickQueryFontMetrics - if isinstance(text, text_type): - if self.text_encoding: - text = text.encode(self.text_encoding) - else: - text = binary(text) - result = font_metrics_f(image.wand, self.resource, text) - if not result: # pragma: no cover - # Error on drawing context - self.raise_exception() - # Or error on image canvas - image.raise_exception() - # Generate a generic error if ImageMagick couldn't emit one. - raise ValueError('Unable to render text with current font.') - args = [result[i] for i in xrange(13)] - result = library.MagickRelinquishMemory(result) - return FontMetrics(*args) - - def line(self, start, end): - """Draws a line ``start`` to ``end``. - - :param start: (:class:`~numbers.Integral`, :class:`numbers.Integral`) - pair which represents starting x and y of the line - :type start: :class:`collections.abc.Sequence` - :param end: (:class:`~numbers.Integral`, :class:`numbers.Integral`) - pair which represents ending x and y of the line - :type end: :class:`collections.abc.Sequence` - - """ - start_x, start_y = start - end_x, end_y = end - library.DrawLine(self.resource, - int(start_x), int(start_y), - int(end_x), int(end_y)) - - def matte(self, x=0.0, y=0.0, paint_method='undefined'): - """Paints on the image's opacity channel in order to set effected - pixels to transparent. - - To influence the opacity of pixels. The available methods are: - - - ``'undefined'`` - - ``'point'`` - - ``'replace'`` - - ``'floodfill'`` - - ``'filltoborder'`` - - ``'reset'`` - - .. note:: - - This method has been replace by :meth:`alpha()` in ImageMagick - version 7. An :class:`AttributeError` will be raised if attempting - to call on a library without ``DrawMatte`` support. - - .. versionadded:: 0.4.0 - - """ - if library.DrawMatte is None: - raise AttributeError( - 'Method removed from ImageMagick version. ' + - 'Please use `wand.drawing.Drawing.alpha()\' instead.' - ) - assertions.assert_real(x=x, y=y) - assertions.string_in_list(PAINT_METHOD_TYPES, - 'wand.drawing.PAINT_METHOD_TYPES', - paint_method=paint_method) - op = PAINT_METHOD_TYPES.index(paint_method) - library.DrawMatte(self.resource, x, y, op) - - def path_close(self): - """Adds a path element to the current path which closes - the current subpath by drawing a straight line from the current point - to the current subpath's most recent starting point. - - .. versionadded:: 0.4.0 - - """ - library.DrawPathClose(self.resource) - return self - - def path_curve(self, to=None, controls=None, smooth=False, relative=False): - """Draws a cubic Bezier curve from the current point to given ``to`` - (x,y) coordinate using ``controls`` points at the beginning and - the end of the curve. - If ``smooth`` is set to True, only one ``controls`` is expected - and the previous control is used, else two pair of coordinates are - expected to define the control points. The ``to`` coordinate then - becomes the new current point. - - :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to - :type to: :class:`collections.abc.Sequence` - :param controls: (:class:`~numbers.Real`, :class:`numbers.Real`) - coordinate to used to influence curve - :type controls: :class:`collections.abc.Sequence` - :param smooth: :class:`bool` assume last defined control coordinate - :type smooth: :class:`bool` - :param relative: treat given coordinates as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - if to is None: - raise TypeError('to is missing') - if controls is None: - raise TypeError('controls is missing') - x, y = to - if smooth: - x2, y2 = controls - else: - (x1, y1), (x2, y2) = controls - - if smooth: - if relative: - library.DrawPathCurveToSmoothRelative(self.resource, - x2, y2, x, y) - else: - library.DrawPathCurveToSmoothAbsolute(self.resource, - x2, y2, x, y) - else: - if relative: - library.DrawPathCurveToRelative(self.resource, - x1, y1, x2, y2, x, y) - else: - library.DrawPathCurveToAbsolute(self.resource, - x1, y1, x2, y2, x, y) - return self - - def path_curve_to_quadratic_bezier(self, to=None, control=None, - smooth=False, relative=False): - """Draws a quadratic Bezier curve from the current point to given - ``to`` coordinate. The control point is assumed to be the reflection of - the control point on the previous command if ``smooth`` is True, else a - pair of ``control`` coordinates must be given. Each coordinates can be - relative, or absolute, to the current point by setting the ``relative`` - flag. The ``to`` coordinate then becomes the new current point, and the - ``control`` coordinate will be assumed when called again - when ``smooth`` is set to true. - - :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to - :type to: :class:`collections.abc.Sequence` - :param control: (:class:`~numbers.Real`, :class:`numbers.Real`) - coordinate to used to influence curve - :type control: :class:`collections.abc.Sequence` - :param smooth: assume last defined control coordinate - :type smooth: :class:`bool` - :param relative: treat given coordinates as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - if to is None: - raise TypeError('to is missing') - x, y = to - - if smooth: - if relative: - library.DrawPathCurveToQuadraticBezierSmoothRelative( - self.resource, float(x), float(y) - ) - else: - library.DrawPathCurveToQuadraticBezierSmoothAbsolute( - self.resource, float(x), float(y) - ) - else: - if control is None: - raise TypeError('control is missing') - x1, y1 = control - if relative: - library.DrawPathCurveToQuadraticBezierRelative(self.resource, - float(x1), - float(y1), - float(x), - float(y)) - else: - library.DrawPathCurveToQuadraticBezierAbsolute(self.resource, - float(x1), - float(y1), - float(x), - float(y)) - return self - - def path_elliptic_arc(self, to=None, radius=None, rotation=0.0, - large_arc=False, clockwise=False, relative=False): - """Draws an elliptical arc from the current point to given ``to`` - coordinates. The ``to`` coordinates can be relative, or absolute, - to the current point by setting the ``relative`` flag. - The size and orientation of the ellipse are defined by - two radii (rx, ry) in ``radius`` and an ``rotation`` parameters, - which indicates how the ellipse as a whole is - rotated relative to the current coordinate system. The center of the - ellipse is calculated automagically to satisfy the constraints imposed - by the other parameters. ``large_arc`` and ``clockwise`` contribute to - the automatic calculations and help determine how the arc is drawn. - If ``large_arc`` is True then draw the larger of the available arcs. - If ``clockwise`` is true, then draw the arc matching a clock-wise - rotation. - - :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to - :type to: :class:`collections.abc.Sequence` - :param radius: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents the radii of the ellipse to draw - :type radius: :class:`collections.abc.Sequence` - :param rotate: degree to rotate ellipse on x-axis - :type rotate: :class:`~numbers.Real` - :param large_arc: draw largest available arc - :type large_arc: :class:`bool` - :param clockwise: draw arc path clockwise from start to target - :type clockwise: :class:`bool` - :param relative: treat given coordinates as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - if to is None: - raise TypeError('to is missing') - if radius is None: - raise TypeError('radius is missing') - x, y = to - rx, ry = radius - if relative: - library.DrawPathEllipticArcRelative(self.resource, - float(rx), float(ry), - float(rotation), - bool(large_arc), - bool(clockwise), - float(x), float(y)) - else: - library.DrawPathEllipticArcAbsolute(self.resource, - float(rx), float(ry), - float(rotation), - bool(large_arc), - bool(clockwise), - float(x), float(y)) - return self - - def path_finish(self): - """Terminates the current path. - - .. versionadded:: 0.4.0 - - """ - library.DrawPathFinish(self.resource) - return self - - def path_horizontal_line(self, x=None, relative=False): - """Draws a horizontal line path from the current point to the target - point. Given ``x`` parameter can be relative, or absolute, to the - current point by setting the ``relative`` flag. The target point then - becomes the new current point. - - :param x: :class:`~numbers.Real` - x-axis point to draw to. - :type x: :class:`~numbers.Real` - :param relative: :class:`bool` - treat given point as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_real(x=x) - if relative: - library.DrawPathLineToHorizontalRelative(self.resource, x) - else: - library.DrawPathLineToHorizontalAbsolute(self.resource, x) - return self - - def path_line(self, to=None, relative=False): - """Draws a line path from the current point to the given ``to`` - coordinate. The ``to`` coordinates can be relative, or absolute, to the - current point by setting the ``relative`` flag. The coordinate then - becomes the new current point. - - :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to. - :type to: :class:`collections.abc.Sequence` - :param relative: :class:`bool` - treat given coordinates as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_coordinate(to=to) - x, y = to - if relative: - library.DrawPathLineToRelative(self.resource, x, y) - else: - library.DrawPathLineToAbsolute(self.resource, x, y) - return self - - def path_move(self, to=None, relative=False): - """Starts a new sub-path at the given coordinates. Given ``to`` - parameter can be relative, or absolute, by setting the ``relative`` - flag. - - :param to: (:class:`~numbers.Real`, :class:`numbers.Real`) - pair which represents coordinates to draw to. - :type to: :class:`collections.abc.Sequence` - :param relative: :class:`bool` - treat given coordinates as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_coordinate(to=to) - x, y = to - if relative: - library.DrawPathMoveToRelative(self.resource, x, y) - else: - library.DrawPathMoveToAbsolute(self.resource, x, y) - return self - - def path_start(self): - """Declares the start of a path drawing list which is terminated by a - matching :meth:`path_finish()` command. All other `path_*` commands - must be enclosed between a :meth:`path_start()` and a - :meth:`path_finish()` command. This is because path drawing commands - are subordinate commands and they do not function by themselves. - - .. versionadded:: 0.4.0 - - """ - library.DrawPathStart(self.resource) - return self - - def path_vertical_line(self, y=None, relative=False): - """Draws a vertical line path from the current point to the target - point. Given ``y`` parameter can be relative, or absolute, to the - current point by setting the ``relative`` flag. The target point then - becomes the new current point. - - :param y: :class:`~numbers.Real` - y-axis point to draw to. - :type y: :class:`~numbers.Real` - :param relative: :class:`bool` - treat given point as relative to current point - :type relative: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_real(y=y) - if relative: - library.DrawPathLineToVerticalRelative(self.resource, y) - else: - library.DrawPathLineToVerticalAbsolute(self.resource, y) - return self - - def polygon(self, points=None): - """Draws a polygon using the current :attr:`stroke_color`, - :attr:`stroke_width`, and :attr:`fill_color`, using the specified - array of coordinates. - - Example polygon on ``image`` :: - - with Drawing() as draw: - points = [(40,10), (20,50), (90,10), (70,40)] - draw.polygon(points) - draw.draw(image) - - :param points: list of x,y tuples - :type points: :class:`list` - - .. versionadded:: 0.4.0 - - """ - - (points_l, points_p) = _list_to_point_info(points) - library.DrawPolygon(self.resource, points_l, - ctypes.cast(points_p, ctypes.POINTER(PointInfo))) - - def polyline(self, points=None): - """Draws a polyline using the current :attr:`stroke_color`, - :attr:`stroke_width`, and :attr:`fill_color`, using the specified - array of coordinates. - - Identical to :class:`~wand.drawing.Drawing.polygon`, but without closed - stroke line. - - :param points: list of x,y tuples - :type points: :class:`list` - - .. versionadded:: 0.4.0 - - """ - - (points_l, points_p) = _list_to_point_info(points) - library.DrawPolyline(self.resource, points_l, - ctypes.cast(points_p, ctypes.POINTER(PointInfo))) - - def point(self, x, y): - """Draws a point at given ``x`` and ``y`` - - :param x: :class:`~numbers.Real` x of point - :type x: :class:`~numbers.Real` - :param y: :class:`~numbers.Real` y of point - :type y: :class:`~numbers.Real` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_real(x=x, y=y) - library.DrawPoint(self.resource, x, y) - - def pop(self): - """Pop destroys the current tip of the drawing context stack, - and restores the parent style context. - See :meth:`push()` method for an example. - - .. note:: - - Popping the graphical context stack will not erase, - or alter, any previously executed drawing commands. - - :returns: success of pop operation. - :rtype: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - ok = bool(library.PopDrawingWand(self.resource)) - if not ok: # pragma: no cover - self.raise_exception() - return ok - - def pop_clip_path(self): - """Terminates a clip path definition. - - .. versionadded:: 0.4.0 - - """ - library.DrawPopClipPath(self.resource) - - def pop_defs(self): - """Terminates a definition list. - - .. versionadded:: 0.4.0 - - """ - library.DrawPopDefs(self.resource) - - def pop_pattern(self): - """Terminates a pattern definition. - - .. versionadded:: 0.4.0 - - """ - library.DrawPopPattern(self.resource) - - def push(self): - """Grows the current drawing context stack by one, and inherits - the previous style attributes. Use :class:`Drawing.pop` to return - to restore previous style attributes. - - This is useful for drawing shapes with diffrent styles - without repeatedly setting the similar - :meth:`fill_color ` & - :meth:`stroke_color ` properties. - - For example:: - - with Drawing() as ctx: - ctx.fill_color = Color('GREEN') - ctx.stroke_color = Color('ORANGE') - ctx.push() - ctx.fill_color = Color('RED') - ctx.text(x1, y1, 'this is RED with ORANGE outline') - ctx.push() - ctx.stroke_color = Color('BLACK') - ctx.text(x2, y2, 'this is RED with BLACK outline') - ctx.pop() - ctx.pop() - ctx.text(x3, y3, 'this is GREEN with ORANGE outline') - - Which translate to the following MVG:: - - push graphic-context - fill "GREEN" - stroke "ORANGE" - push graphic-context - fill "RED" - text x1,y1 "this is RED with ORANGE outline" - push graphic-context - stroke "BLACK" - text x2,y2 "this is RED with BLACK outline" - pop graphic-context - pop graphic-context - text x3,y3 "this is GREEN with ORANGE outline" - pop graphic-context - - .. note:: - - Pushing graphical context does not reset any previously - drawn artifacts. - - :returns: success of push operation. - :rtype: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - ok = bool(library.PushDrawingWand(self.resource)) - if not ok: - self.raise_exception() - return ok - - def push_clip_path(self, clip_mask_id): - """Starts a clip path definition which is comprised of any number of - drawing commands and terminated by a :class:`Drawing.pop_clip_path` - command. - - :param clip_mask_id: string identifier to associate with the clip path. - :type clip_mask_id: :class:`basestring` - - .. versionadded:: 0.4.0 - - """ - library.DrawPushClipPath(self.resource, binary(clip_mask_id)) - - def push_defs(self): - """Indicates that commands up to a terminating - :class:`Drawing.pop_defs` command create named elements (e.g. - clip-paths, textures, etc.) which may safely be processed earlier for - the sake of efficiency. - - .. versionadded:: 0.4.0 - - """ - library.DrawPushDefs(self.resource) - - def push_pattern(self, pattern_id, left, top, width, height): - """Indicates that subsequent commands up to a - :class:`Drawing.pop_pattern` command comprise the definition of a named - pattern. The pattern space is assigned top left corner coordinates, a - width and height, and becomes its own drawing space. Anything which can - be drawn may be used in a pattern definition. - Named patterns may be used as stroke or brush definitions. - - :param pattern_id: a unique identifier for the pattern. - :type pattern_id: :class:`basestring` - :param left: x ordinate of top left corner. - :type left: :class:`numbers.Real` - :param top: y ordinate of top left corner. - :type top: :class:`numbers.Real` - :param width: width of pattern space. - :type width: :class:`numbers.Real` - :param height: height of pattern space. - :type height: :class:`numbers.Real` - :returns: success of push operation - :rtype: :class:`bool` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_string(pattern_id=pattern_id) - assertions.assert_real(left=left, top=top, width=width, height=height) - okay = library.DrawPushPattern(self.resource, binary(pattern_id), - left, top, - width, height) - if not okay: # pragma: no cover - self.raise_exception() - return bool(okay) - - def rectangle(self, left=None, top=None, right=None, bottom=None, - width=None, height=None, radius=None, xradius=None, - yradius=None): - """Draws a rectangle using the current :attr:`stroke_color`, - :attr:`stroke_width`, and :attr:`fill_color`. - - .. sourcecode:: text - - +--------------------------------------------------+ - | ^ ^ | - | | | | - | top | | - | | | | - | v | | - | <-- left --> +-------------------+ bottom | - | | ^ | | | - | | <-- width --|---> | | | - | | height | | | - | | | | | | - | | v | | | - | +-------------------+ v | - | <--------------- right ----------> | - +--------------------------------------------------+ - - :param left: x-offset of the rectangle to draw - :type left: :class:`numbers.Real` - :param top: y-offset of the rectangle to draw - :type top: :class:`numbers.Real` - :param right: second x-offset of the rectangle to draw. - this parameter and ``width`` parameter are exclusive - each other - :type right: :class:`numbers.Real` - :param bottom: second y-offset of the rectangle to draw. - this parameter and ``height`` parameter are exclusive - each other - :type bottom: :class:`numbers.Real` - :param width: the :attr:`width` of the rectangle to draw. - this parameter and ``right`` parameter are exclusive - each other - :type width: :class:`numbers.Real` - :param height: the :attr:`height` of the rectangle to draw. - this parameter and ``bottom`` parameter are exclusive - each other - :type height: :class:`numbers.Real` - :param radius: the corner rounding. this is a short-cut for setting - both :attr:`xradius`, and :attr:`yradius` - :type radius: :class:`numbers.Real` - :param xradius: the :attr:`xradius` corner in horizontal direction. - :type xradius: :class:`numbers.Real` - :param yradius: the :attr:`yradius` corner in vertical direction. - :type yradius: :class:`numbers.Real` - - .. versionadded:: 0.3.6 - - .. versionchanged:: 0.4.0 - Radius keywords added to create rounded rectangle. - - """ - if left is None: - raise TypeError('left is missing') - elif top is None: - raise TypeError('top is missing') - elif right is None and width is None: - raise TypeError('right/width is missing') - elif bottom is None and height is None: - raise TypeError('bottom/height is missing') - elif not (right is None or width is None): - raise TypeError('parameters right and width are exclusive each ' - 'other; use one at a time') - elif not (bottom is None or height is None): - raise TypeError('parameters bottom and height are exclusive each ' - 'other; use one at a time') - elif not isinstance(left, numbers.Real): - raise TypeError('left must be numbers.Real, not ' + repr(left)) - elif not isinstance(top, numbers.Real): - raise TypeError('top must be numbers.Real, not ' + repr(top)) - elif not (right is None or isinstance(right, numbers.Real)): - raise TypeError('right must be numbers.Real, not ' + repr(right)) - elif not (bottom is None or isinstance(bottom, numbers.Real)): - raise TypeError('bottom must be numbers.Real, not ' + repr(bottom)) - elif not (width is None or isinstance(width, numbers.Real)): - raise TypeError('width must be numbers.Real, not ' + repr(width)) - elif not (height is None or isinstance(height, numbers.Real)): - raise TypeError('height must be numbers.Real, not ' + repr(height)) - if right is None: - if width < 0: - raise ValueError('width must be positive, not ' + repr(width)) - right = left + width - elif right < left: - raise ValueError('right must be more than left ({0!r}), ' - 'not {1!r})'.format(left, right)) - if bottom is None: - if height < 0: - raise ValueError('height must be positive, not ' + - repr(height)) - bottom = top + height - elif bottom < top: - raise ValueError('bottom must be more than top ({0!r}), ' - 'not {1!r})'.format(top, bottom)) - if radius is not None: - xradius = yradius = radius - if xradius is not None or yradius is not None: - if xradius is None: - xradius = 0.0 - if yradius is None: - yradius = 0.0 - assertions.assert_real(xradius=xradius, yradius=yradius) - library.DrawRoundRectangle(self.resource, left, top, right, bottom, - xradius, yradius) - else: - library.DrawRectangle(self.resource, left, top, right, bottom) - self.raise_exception() - - def rotate(self, degree=0.0): - """Applies the specified rotation to the current coordinate space. - - :param degree: degree to rotate - :type degree: :class:`~numbers.Real` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_real(degree=degree) - library.DrawRotate(self.resource, degree) - - def scale(self, x=1.0, y=1.0): - """ - Adjusts the scaling factor to apply in the horizontal and vertical - directions to the current coordinate space. - - :param x: Horizontal scale factor. Default `1.0` - :type x: :class:`~numbers.Real` - :param y: Vertical scale factor. Default `1.0` - :type y: :class:`~numbers.Real` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_real(x=x, y=y) - library.DrawScale(self.resource, x, y) - - def set_fill_pattern_url(self, url): - """Sets the URL to use as a fill pattern for filling objects. Only - local URLs ("#identifier") are supported at this time. These local URLs - are normally created by defining a named fill pattern with - Drawing.push_pattern & Drawing.pop_pattern. - - :param url: URL to use to obtain fill pattern. - :type url: :class:`basestring` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_string(url=url) - if url[0] != '#': - raise ValueError('value not a relative URL, ' - 'expecting "#identifier"') - library.DrawSetFillPatternURL(self.resource, binary(url)) - self.raise_exception() - - def set_stroke_pattern_url(self, url): - """Sets the pattern used for stroking object outlines. Only local - URLs ("#identifier") are supported at this time. These local URLs are - normally created by defining a named stroke pattern with - Drawing.push_pattern & Drawing.pop_pattern. - - :param url: URL to use to obtain stroke pattern. - :type url: :class:`basestring` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_string(url=url) - if url[0] != '#': - raise ValueError('value not a relative URL, ' - 'expecting "#identifier"') - library.DrawSetStrokePatternURL(self.resource, binary(url)) - self.raise_exception() - - def skew(self, x=None, y=None): - """Skews the current coordinate system in the horizontal direction if - ``x`` is given, and vertical direction if ``y`` is given. - - :param x: Skew horizontal direction - :type x: :class:`~numbers.Real` - :param y: Skew vertical direction - :type y: :class:`~numbers.Real` - - .. versionadded:: 0.4.0 - - """ - if x is not None: - assertions.assert_real(x=x) - library.DrawSkewX(self.resource, x) - if y is not None: - assertions.assert_real(y=y) - library.DrawSkewY(self.resource, y) - - def text(self, x, y, body): - """Writes a text ``body`` into (``x``, ``y``). - - :param x: the left offset where to start writing a text - :type x: :class:`numbers.Integral` - :param y: the baseline where to start writing text - :type y: :class:`numbers.Integral` - :param body: the body string to write - :type body: :class:`basestring` - - """ - assertions.assert_unsigned_integer(x=x, y=y) - assertions.assert_string(body=body) - if not body: - raise ValueError('body string cannot be empty') - if isinstance(body, text_type): - # According to ImageMagick C API docs, we can use only UTF-8 - # at this time, so we do hardcoding here. - # http://imagemagick.org/api/drawing-wand.php#DrawSetTextEncoding - if not self.text_encoding: - self.text_encoding = 'UTF-8' - body = body.encode(self.text_encoding) - body_p = ctypes.create_string_buffer(body) - library.DrawAnnotation( - self.resource, x, y, - ctypes.cast(body_p, ctypes.POINTER(ctypes.c_ubyte)) - ) - - def translate(self, x=0.0, y=0.0): - """Applies a translation to the current coordinate system which moves - the coordinate system origin to the specified coordinate. - - :param x: Skew horizontal direction - :type x: :class:`~numbers.Real` - :param y: Skew vertical direction - :type y: :class:`~numbers.Real` - - .. versionadded:: 0.4.0 - """ - assertions.assert_real(x=x, y=y) - library.DrawTranslate(self.resource, x, y) - - def viewbox(self, left, top, right, bottom): - """Viewbox sets the overall canvas size to be recorded with the drawing - vector data. Usually this will be specified using the same size as the - canvas image. When the vector data is saved to SVG or MVG formats, the - viewbox is use to specify the size of the canvas image that a viewer - will render the vector data on. - - :param left: the left most point of the viewbox. - :type left: :class:`~numbers.Integral` - :param top: the top most point of the viewbox. - :type top: :class:`~numbers.Integral` - :param right: the right most point of the viewbox. - :type right: :class:`~numbers.Integral` - :param bottom: the bottom most point of the viewbox. - :type bottom: :class:`~numbers.Integral` - - .. versionadded:: 0.4.0 - - """ - assertions.assert_integer(left=left, top=top, - right=right, bottom=bottom) - library.DrawSetViewbox(self.resource, left, top, right, bottom) - - def __call__(self, image): - return self.draw(image) - - -class FontMetrics(collections.namedtuple('FontMetrics', - FONT_METRICS_ATTRIBUTES)): - """ - The tuple subtype which consists of font metrics data. - - Pixel values can be converted to points by the following: - - .. math:: - - points = \\frac{pixels \\ast 72}{resolution} - - .. py:attribute:: character_width - - (:class:`numbers.Real`) The horizontal value of the ``x`` in pixels. - - .. py:attribute:: character_height - - (:class:`numbers.Real`) The vertical value of the ``x`` in pixels. - - .. py:attribute:: ascender - - (:class:`numbers.Real`) The largest distance from the text baseline - to the highest point above the x-height. This value should always be - positive. - - .. py:attribute:: descender - - (:class:`numbers.Real`) The farthest distance from the text baseline - to the lowest point below the x-height. This value should always be - negative. - - .. py:attribute:: text_width - - (:class:`numbers.Real`) The full width of the text. - - .. py:attribute:: text_height - - (:class:`numbers.Real`) The full height of the text. - - .. py:attribute:: maximum_horizontal_advance - - (:class:`numbers.Real`) The largest distance from the start of one - character to the start of the following character. - - .. py:attribute:: x1 - - (:class:`numbers.Real`) The horizontal value of the bounding box's - starting corner. - - .. py:attribute:: y1 - - (:class:`numbers.Real`) The vertical value of the bounding box's - starting corner. - - .. py:attribute:: x2 - - (:class:`numbers.Real`) The horizontal value of the bounding box's - ending corner. - - .. py:attribute:: y2 - - (:class:`numbers.Real`) The vertical value of the bounding box's - ending corner. - - .. py:attribute:: x - - (:class:`numbers.Real`) The horizontal value of the origin. - - .. py:attribute:: y - - (:class:`numbers.Real`) The vertical value of the origin. - - .. note:: - - When using :meth:`~wand.drawing.Drawing.get_font_metrics`, the returned - attributes ``x1``, ``y1``, ``x2``, ``y2``, ``x`` and ``y`` can be - ignored as they are used for internal FreeType rendering, and have no - meaningful context. - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.6.8 - Created sub-class of the namedtuple to improve auto-documentation. - """ - def size(self): - """Short-cut method for the width & height of the rendered text. - - :returns: (width, height) - - .. versionadded:: 0.6.8 - """ - return self.text_width, self.text_height - - -def _list_to_point_info(points): - """ - Helper method to convert a list of tuples to ``const * PointInfo`` - - :param points: a list of tuples - :type points: `list` - :returns: tuple of point length and c_double array - :rtype: `tuple` - :raises: `TypeError` - - .. versionadded:: 0.4.0 - - """ - if not isinstance(points, list): - raise TypeError('points must be a list, not ' + repr(points)) - point_length = len(points) - tuple_size = 2 - point_info_size = point_length * tuple_size - # Allocate sequence of memory - point_info = (ctypes.c_double * point_info_size)() - for double_index in xrange(point_info_size): - tuple_index = double_index // tuple_size - tuple_offset = double_index % tuple_size - point_info[double_index] = ctypes.c_double( - points[tuple_index][tuple_offset] - ) - return (point_length, point_info) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/exceptions.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/exceptions.py deleted file mode 100644 index efa3a2c..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/exceptions.py +++ /dev/null @@ -1,449 +0,0 @@ -""":mod:`wand.exceptions` --- Errors and warnings -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This module maps MagickWand API's errors and warnings to Python's native -exceptions and warnings. You can catch all MagickWand errors using Python's -natural way to catch errors. - -.. seealso:: - - `ImageMagick Exceptions `_ - -.. versionadded:: 0.1.1 - -.. versionchanged:: 0.5.8 - Warning & Error Exceptions are now explicitly defined. Previously - ImageMagick domain-based errors were dynamically generated at runtime. -""" - - -class WandException(Exception): - """All Wand-related exceptions are derived from this class.""" - - -class BaseWarning(WandException, Warning): - """Base class for Wand-related warnings. - - .. versionadded:: 0.4.4 - - """ - - -class BaseError(WandException): - """Base class for Wand-related errors. - - .. versionadded:: 0.4.4 - - """ - - -class BaseFatalError(WandException): - """Base class for Wand-related fatal errors. - - .. versionadded:: 0.4.4 - - """ - - -class WandLibraryVersionError(WandException): - """Base class for Wand-related ImageMagick version errors. - - .. versionadded:: 0.3.2 - - """ - - -class WandRuntimeError(WandException, RuntimeError): - """Generic class for Wand-related runtime errors. - - .. versionadded:: 0.5.2 - """ - - -class ResourceLimitWarning(BaseWarning, MemoryError): - """A program resource is exhausted e.g. not enough memory.""" - wand_error_code = 300 - - -class ResourceLimitError(BaseError, MemoryError): - """A program resource is exhausted e.g. not enough memory.""" - wand_error_code = 400 - - -class ResourceLimitFatalError(BaseFatalError, MemoryError): - """A program resource is exhausted e.g. not enough memory.""" - wand_error_code = 700 - - -class TypeWarning(BaseWarning): - """A font is unavailable; a substitution may have occurred.""" - wand_error_code = 305 - - -class TypeError(BaseError): - """A font is unavailable; a substitution may have occurred.""" - wand_error_code = 405 - - -class TypeFatalError(BaseFatalError): - """A font is unavailable; a substitution may have occurred.""" - wand_error_code = 705 - - -class OptionWarning(BaseWarning): - """A command-line option was malformed.""" - wand_error_code = 310 - - -class OptionError(BaseError): - """A command-line option was malformed.""" - wand_error_code = 410 - - -class OptionFatalError(BaseFatalError): - """A command-line option was malformed.""" - wand_error_code = 710 - - -class DelegateWarning(BaseWarning): - """An ImageMagick delegate failed to complete.""" - wand_error_code = 315 - - -class DelegateError(BaseError): - """An ImageMagick delegate failed to complete.""" - wand_error_code = 415 - - -class DelegateFatalError(BaseFatalError): - """An ImageMagick delegate failed to complete.""" - wand_error_code = 715 - - -class MissingDelegateWarning(BaseWarning, ImportError): - """The image type can not be read or written because the appropriate; - delegate is missing.""" - wand_error_code = 320 - - -class MissingDelegateError(BaseError, ImportError): - """The image type can not be read or written because the appropriate; - delegate is missing.""" - wand_error_code = 420 - - -class MissingDelegateFatalError(BaseFatalError, ImportError): - """The image type can not be read or written because the appropriate; - delegate is missing.""" - wand_error_code = 720 - - -class CorruptImageWarning(BaseWarning, ValueError): - """The image file may be corrupt.""" - wand_error_code = 325 - - -class CorruptImageError(BaseError, ValueError): - """The image file may be corrupt.""" - wand_error_code = 425 - - -class CorruptImageFatalError(BaseFatalError, ValueError): - """The image file may be corrupt.""" - wand_error_code = 725 - - -class FileOpenWarning(BaseWarning, IOError): - """The image file could not be opened for reading or writing.""" - wand_error_code = 330 - - -class FileOpenError(BaseError, IOError): - """The image file could not be opened for reading or writing.""" - wand_error_code = 430 - - -class FileOpenFatalError(BaseFatalError, IOError): - """The image file could not be opened for reading or writing.""" - wand_error_code = 730 - - -class BlobWarning(BaseWarning, IOError): - """A binary large object could not be allocated, read, or written.""" - wand_error_code = 335 - - -class BlobError(BaseError, IOError): - """A binary large object could not be allocated, read, or written.""" - wand_error_code = 435 - - -class BlobFatalError(BaseFatalError, IOError): - """A binary large object could not be allocated, read, or written.""" - wand_error_code = 735 - - -class StreamWarning(BaseWarning, IOError): - """There was a problem reading or writing from a stream.""" - wand_error_code = 340 - - -class StreamError(BaseError, IOError): - """There was a problem reading or writing from a stream.""" - wand_error_code = 440 - - -class StreamFatalError(BaseFatalError, IOError): - """There was a problem reading or writing from a stream.""" - wand_error_code = 740 - - -class CacheWarning(BaseWarning): - """Pixels could not be read or written to the pixel cache.""" - wand_error_code = 345 - - -class CacheError(BaseError): - """Pixels could not be read or written to the pixel cache.""" - wand_error_code = 445 - - -class CacheFatalError(BaseFatalError): - """Pixels could not be read or written to the pixel cache.""" - wand_error_code = 745 - - -class CoderWarning(BaseWarning): - """There was a problem with an image coder.""" - wand_error_code = 350 - - -class CoderError(BaseError): - """There was a problem with an image coder.""" - wand_error_code = 450 - - -class CoderFatalError(BaseFatalError): - """There was a problem with an image coder.""" - wand_error_code = 750 - - -class ModuleWarning(BaseWarning): - """There was a problem with an image module.""" - wand_error_code = 355 - - -class ModuleError(BaseError): - """There was a problem with an image module.""" - wand_error_code = 455 - - -class ModuleFatalError(BaseFatalError): - """There was a problem with an image module.""" - wand_error_code = 755 - - -class DrawWarning(BaseWarning): - """A drawing operation failed.""" - wand_error_code = 360 - - -class DrawError(BaseError): - """A drawing operation failed.""" - wand_error_code = 460 - - -class DrawFatalError(BaseFatalError): - """A drawing operation failed.""" - wand_error_code = 760 - - -class ImageWarning(BaseWarning): - """The operation could not complete due to an incompatible image.""" - wand_error_code = 365 - - -class ImageError(BaseError): - """The operation could not complete due to an incompatible image.""" - wand_error_code = 465 - - -class ImageFatalError(BaseFatalError): - """The operation could not complete due to an incompatible image.""" - wand_error_code = 765 - - -class WandWarning(BaseWarning): - """There was a problem specific to the MagickWand API.""" - wand_error_code = 370 - - -class WandError(BaseError): - """There was a problem specific to the MagickWand API.""" - wand_error_code = 470 - - -class WandFatalError(BaseFatalError): - """There was a problem specific to the MagickWand API.""" - wand_error_code = 770 - - -class RandomWarning(BaseWarning): - """There is a problem generating a true or pseudo-random number.""" - wand_error_code = 375 - - -class RandomError(BaseError): - """There is a problem generating a true or pseudo-random number.""" - wand_error_code = 475 - - -class RandomFatalError(BaseFatalError): - """There is a problem generating a true or pseudo-random number.""" - wand_error_code = 775 - - -class XServerWarning(BaseWarning): - """An X resource is unavailable.""" - wand_error_code = 380 - - -class XServerError(BaseError): - """An X resource is unavailable.""" - wand_error_code = 480 - - -class XServerFatalError(BaseFatalError): - """An X resource is unavailable.""" - wand_error_code = 780 - - -class MonitorWarning(BaseWarning): - """There was a problem activating the progress monitor.""" - wand_error_code = 385 - - -class MonitorError(BaseError): - """There was a problem activating the progress monitor.""" - wand_error_code = 485 - - -class MonitorFatalError(BaseFatalError): - """There was a problem activating the progress monitor.""" - wand_error_code = 785 - - -class RegistryWarning(BaseWarning): - """There was a problem getting or setting the registry.""" - wand_error_code = 390 - - -class RegistryError(BaseError): - """There was a problem getting or setting the registry.""" - wand_error_code = 490 - - -class RegistryFatalError(BaseFatalError): - """There was a problem getting or setting the registry.""" - wand_error_code = 790 - - -class ConfigureWarning(BaseWarning): - """There was a problem getting a configuration file.""" - wand_error_code = 395 - - -class ConfigureError(BaseError): - """There was a problem getting a configuration file.""" - wand_error_code = 495 - - -class ConfigureFatalError(BaseFatalError): - """There was a problem getting a configuration file.""" - wand_error_code = 795 - - -class PolicyWarning(BaseWarning): - """A policy denies access to a delegate, coder, filter, path, or - resource.""" - wand_error_code = 399 - - -class PolicyError(BaseError): - """A policy denies access to a delegate, coder, filter, path, or - resource.""" - wand_error_code = 499 - - -class PolicyFatalError(BaseFatalError): - """A policy denies access to a delegate, coder, filter, path, or - resource.""" - wand_error_code = 799 - - -#: (:class:`dict`) The dictionary of (code, exc_type). -TYPE_MAP = { - 300: ResourceLimitWarning, - 305: TypeWarning, - 310: OptionWarning, - 315: DelegateWarning, - 320: MissingDelegateWarning, - 325: CorruptImageWarning, - 330: FileOpenWarning, - 335: BlobWarning, - 340: StreamWarning, - 345: CacheWarning, - 350: CoderWarning, - 355: ModuleWarning, - 360: DrawWarning, - 365: ImageWarning, - 370: WandWarning, - 375: RandomWarning, - 380: XServerWarning, - 385: MonitorWarning, - 390: RegistryWarning, - 395: ConfigureWarning, - 399: PolicyWarning, - 400: ResourceLimitError, - 405: TypeError, - 410: OptionError, - 415: DelegateError, - 420: MissingDelegateError, - 425: CorruptImageError, - 430: FileOpenError, - 435: BlobError, - 440: StreamError, - 445: CacheError, - 450: CoderError, - 455: ModuleError, - 460: DrawError, - 465: ImageError, - 470: WandError, - 475: RandomError, - 480: XServerError, - 485: MonitorError, - 490: RegistryError, - 495: ConfigureError, - 499: PolicyError, - 700: ResourceLimitFatalError, - 705: TypeFatalError, - 710: OptionFatalError, - 715: DelegateFatalError, - 720: MissingDelegateFatalError, - 725: CorruptImageFatalError, - 730: FileOpenFatalError, - 735: BlobFatalError, - 740: StreamFatalError, - 745: CacheFatalError, - 750: CoderFatalError, - 755: ModuleFatalError, - 760: DrawFatalError, - 765: ImageFatalError, - 770: WandFatalError, - 775: RandomFatalError, - 780: XServerFatalError, - 785: MonitorFatalError, - 790: RegistryFatalError, - 795: ConfigureFatalError, - 799: PolicyFatalError, -} diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/font.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/font.py deleted file mode 100644 index e1790fc..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/font.py +++ /dev/null @@ -1,124 +0,0 @@ -""":mod:`wand.font` --- Fonts -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.3.0 - -:class:`Font` is an object which takes the :attr:`~Font.path` of font file, -:attr:`~Font.size`, :attr:`~Font.color`, and whether to use -:attr:`~Font.antialias`\\ ing. If you want to use font by its name rather -than the file path, use TTFQuery_ package. The font path resolution by its -name is a very complicated problem to achieve. - -.. seealso:: - - TTFQuery_ --- Find and Extract Information from TTF Files - TTFQuery builds on the `FontTools-TTX`_ package to allow the Python - programmer to accomplish a number of tasks: - - - query the system to find installed fonts - - - retrieve metadata about any TTF font file - - - this includes the glyph outlines (shape) of individual code-points, - which allows for rendering the glyphs in 3D (such as is done in - OpenGLContext) - - - lookup/find fonts by: - - - abstract family type - - proper font name - - - build simple metadata registries for run-time font matching - -.. _TTFQuery: http://ttfquery.sourceforge.net/ -.. _FontTools-TTX: http://sourceforge.net/projects/fonttools/ - -""" -from . import assertions -from .color import Color -from .compat import string_type, text - -__all__ = ('Font',) - - -class Font(tuple): - """Font struct which is a subtype of :class:`tuple`. - - :param path: the path of the font file - :type path: :class:`str`, :class:`basestring` - :param size: the size of typeface. 0 by default which means *autosized* - :type size: :class:`numbers.Real` - :param color: the color of typeface. black by default - :type color: :class:`~wand.color.Color` - :param antialias: whether to use antialiasing. :const:`True` by default - :type antialias: :class:`bool` - :param stroke_color: optional color to outline typeface. - :type stroke_color: :class:`~wand.color.Color` - :param stroke_width: optional thickness of typeface outline. - :type stroke_width: :class:`numbers.Real` - - .. versionchanged:: 0.3.9 - The ``size`` parameter becomes optional. Its default value is - 0, which means *autosized*. - - .. versionchanged:: 0.5.0 - Added ``stroke_color`` & ``stoke_width`` parameters. - """ - - def __new__(cls, path, size=0, color=None, antialias=True, - stroke_color=None, stroke_width=None): - assertions.assert_string(path=path) - assertions.assert_real(size=size) - if color is None: - color = Color('black') - elif isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - if stroke_color: - if isinstance(stroke_color, string_type): - stroke_color = Color(stroke_color) - assertions.assert_color(stroke_color=stroke_color) - if stroke_width is not None: - assertions.assert_real(stroke_width=stroke_width) - path = text(path) - return tuple.__new__(cls, (path, size, color, bool(antialias), - stroke_color, stroke_width)) - - @property - def path(self): - """(:class:`basestring`) The path of font file.""" - return self[0] - - @property - def size(self): - """(:class:`numbers.Real`) The font size in pixels.""" - return self[1] - - @property - def color(self): - """(:class:`wand.color.Color`) The font color.""" - return self[2] - - @property - def antialias(self): - """(:class:`bool`) Whether to apply antialiasing (``True``) - or not (``False``). - - """ - return self[3] - - @property - def stroke_color(self): - """(:class:`wand.color.Color`) The stroke color.""" - return self[4] - - @property - def stroke_width(self): - """(:class:`numbers.Real`) The width of the stroke line.""" - return self[5] - - def __repr__(self): - return '{0.__module__}.{0.__name__}({1})'.format( - type(self), - tuple.__repr__(self) - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/image.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/image.py deleted file mode 100644 index da0e049..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/image.py +++ /dev/null @@ -1,10870 +0,0 @@ -""":mod:`wand.image` --- Image objects -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Opens and manipulates images. Image objects can be used in :keyword:`with` -statement, and these resources will be automatically managed (even if any -error happened):: - - with Image(filename='pikachu.png') as i: - print('width =', i.width) - print('height =', i.height) - -""" -import ctypes -import functools -import numbers -import weakref - -from . import assertions -from .api import libc, libmagick, library -from .color import Color -from .compat import (abc, binary, binary_type, encode_filename, file_types, - string_type, text, to_bytes, xrange) -from .exceptions import (MissingDelegateError, WandException, - WandRuntimeError, WandLibraryVersionError) -from .font import Font -from .resource import DestroyedResourceError, Resource, genesis -from .cdefs.structures import (CCObjectInfo, CCObjectInfo70A, CCObjectInfo710, - ChannelFeature, GeometryInfo, PixelInfo, - RectangleInfo) -from .version import MAGICK_VERSION_NUMBER, MAGICK_HDRI - - -__all__ = ('ALPHA_CHANNEL_TYPES', 'AUTO_THRESHOLD_METHODS', 'CHANNELS', - 'COLORSPACE_TYPES', 'COMPARE_METRICS', 'COMPOSITE_OPERATORS', - 'COMPRESSION_TYPES', 'DISPOSE_TYPES', 'DISTORTION_METHODS', - 'DITHER_METHODS', 'EVALUATE_OPS', 'FILTER_TYPES', 'FUNCTION_TYPES', - 'GRAVITY_TYPES', 'IMAGE_LAYER_METHOD', 'IMAGE_TYPES', - 'INTERLACE_TYPES', 'KERNEL_INFO_TYPES', 'MORPHOLOGY_METHODS', - 'NOISE_TYPES', 'ORIENTATION_TYPES', 'PAPERSIZE_MAP', - 'PIXEL_INTERPOLATE_METHODS', 'RENDERING_INTENT_TYPES', - 'SPARSE_COLOR_METHODS', 'STATISTIC_TYPES', - 'STORAGE_TYPES', 'VIRTUAL_PIXEL_METHOD', 'UNIT_TYPES', - 'BaseImage', 'ChannelDepthDict', 'ChannelImageDict', - 'ClosedImageError', 'HistogramDict', 'Image', 'ImageProperty', - 'Iterator', 'Metadata', 'OptionDict', 'manipulative', - 'ArtifactTree', 'ProfileDict', 'ConnectedComponentObject') - - -#: (:class:`tuple`) The list of :attr:`~wand.image.BaseImage.alpha_channel` -#: types. -#: -#: - ``'undefined'`` -#: - ``'activate'`` -#: - ``'background'`` -#: - ``'copy'`` -#: - ``'deactivate'`` -#: - ``'discrete'`` - Only available in ImageMagick-7 -#: - ``'extract'`` -#: - ``'off'`` - Only available in ImageMagick-7 -#: - ``'on'`` - Only available in ImageMagick-7 -#: - ``'opaque'`` -#: - ``'reset'`` - Only available in ImageMagick-6 -#: - ``'set'`` -#: - ``'shape'`` -#: - ``'transparent'`` -#: - ``'flatten'`` - Only available in ImageMagick-6 -#: - ``'remove'`` -#: -#: .. seealso:: -#: `ImageMagick Image Channel`__ -#: Describes the SetImageAlphaChannel method which can be used -#: to modify alpha channel. Also describes AlphaChannelType -#: -#: __ http://www.imagemagick.org/api/channel.php#SetImageAlphaChannel -ALPHA_CHANNEL_TYPES = ('undefined', 'activate', 'background', 'copy', - 'deactivate', 'extract', 'opaque', 'reset', 'set', - 'shape', 'transparent', 'flatten', 'remove', - 'associate', 'disassociate') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - ALPHA_CHANNEL_TYPES = ('undefined', 'activate', 'associate', 'background', - 'copy', 'deactivate', 'discrete', 'disassociate', - 'extract', 'off', 'on', 'opaque', 'remove', 'set', - 'shape', 'transparent') - - -#: (:class:`tuple`) The list of methods used by -#: :meth:`Image.auto_threshold() ` -#: -#: - ``'undefined'`` -#: - ``'kapur'`` -#: - ``'otsu'`` -#: - ``'triangle'`` -#: -#: .. versionadded:: 0.5.5 -AUTO_THRESHOLD_METHODS = ('undefined', 'kapur', 'otsu', 'triangle') - - -#: (:class:`dict`) The dictionary of channel types. -#: -#: - ``'undefined'`` -#: - ``'red'`` -#: - ``'gray'`` -#: - ``'cyan'`` -#: - ``'green'`` -#: - ``'magenta'`` -#: - ``'blue'`` -#: - ``'yellow'`` -#: - ``'alpha'`` -#: - ``'opacity'`` -#: - ``'black'`` -#: - ``'index'`` -#: - ``'composite_channels'`` -#: - ``'all_channels'`` -#: - ``'sync_channels'`` -#: - ``'default_channels'`` -#: -#: .. seealso:: -#: -#: `ImageMagick Color Channels`__ -#: Lists the various channel types with descriptions of each -#: -#: __ http://www.imagemagick.org/Magick++/Enumerations.html#ChannelType -#: -#: .. versionchanged:: 0.5.5 -#: Deprecated ``true_alpha``, ``rgb_channels``, and ``gray_channels`` -#: values in favor of MagickCore channel parser. -#: -CHANNELS = dict(undefined=0, red=1, gray=1, cyan=1, green=2, magenta=2, - blue=4, yellow=4, alpha=8, opacity=8, black=32, index=32, - composite_channels=47, all_channels=134217727, true_alpha=64, - rgb=7, rgb_channels=7, gray_channels=1, sync_channels=256, - default_channels=134217719) -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - CHANNELS = dict(undefined=0, red=1, gray=1, cyan=1, green=2, magenta=2, - blue=4, yellow=4, black=8, alpha=16, opacity=16, index=32, - readmask=0x0040, write_mask=128, meta=256, - composite_channels=31, all_channels=134217727, - true_alpha=256, rgb=7, rgb_channels=7, gray_channels=1, - sync_channels=131072, default_channels=134217727) - - -#: (:class:`tuple`) The list of colorspaces. -#: -#: - ``'undefined'`` -#: - ``'rgb'`` -#: - ``'gray'`` -#: - ``'transparent'`` -#: - ``'ohta'`` -#: - ``'lab'`` -#: - ``'xyz'`` -#: - ``'ycbcr'`` -#: - ``'ycc'`` -#: - ``'yiq'`` -#: - ``'ypbpr'`` -#: - ``'yuv'`` -#: - ``'cmyk'`` -#: - ``'srgb'`` -#: - ``'hsb'`` -#: - ``'hsl'`` -#: - ``'hwb'`` -#: - ``'rec601luma'`` - Only available with ImageMagick-6 -#: - ``'rec601ycbcr'`` -#: - ``'rec709luma'`` - Only available with ImageMagick-6 -#: - ``'rec709ycbcr'`` -#: - ``'log'`` -#: - ``'cmy'`` -#: - ``'luv'`` -#: - ``'hcl'`` -#: - ``'lch'`` -#: - ``'lms'`` -#: - ``'lchab'`` -#: - ``'lchuv'`` -#: - ``'scrgb'`` -#: - ``'hsi'`` -#: - ``'hsv'`` -#: - ``'hclp'`` -#: - ``'xyy'`` - Only available with ImageMagick-7 -#: - ``'ydbdr'`` -#: -#: .. seealso:: -#: -#: `ImageMagick Color Management`__ -#: Describes the ImageMagick color management operations -#: -#: __ http://www.imagemagick.org/script/color-management.php -#: -#: .. versionadded:: 0.3.4 -COLORSPACE_TYPES = ('undefined', 'rgb', 'gray', 'transparent', 'ohta', 'lab', - 'xyz', 'ycbcr', 'ycc', 'yiq', 'ypbpr', 'yuv', 'cmyk', - 'srgb', 'hsb', 'hsl', 'hwb', 'rec601luma', 'rec601ycbcr', - 'rec709luma', 'rec709ycbcr', 'log', 'cmy', 'luv', 'hcl', - 'lch', 'lms', 'lchab', 'lchuv', 'scrgb', 'hsi', 'hsv', - 'hclp', 'ydbdr') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - COLORSPACE_TYPES = ('undefined', 'cmy', 'cmyk', 'gray', 'hcl', 'hclp', - 'hsb', 'hsi', 'hsl', 'hsv', 'hwb', 'lab', 'lch', - 'lchab', 'lchuv', 'log', 'lms', 'luv', 'ohta', - 'rec601ycbcr', 'rec709ycbcr', 'rgb', 'scrgb', 'srgb', - 'transparent', 'xyy', 'xyz', 'ycbcr', 'ycc', 'ydbdr', - 'yiq', 'ypbpr', 'yuv') - -#: (:class:`tuple`) The list of compare metric types used by -#: :meth:`Image.compare() ` and -#: :meth:`Image.similarity() ` methods. -#: -#: - ``'undefined'`` -#: - ``'absolute'`` -#: - ``'fuzz'`` -#: - ``'mean_absolute'`` -#: - ``'mean_error_per_pixel'`` -#: - ``'mean_squared'`` -#: - ``'normalized_cross_correlation'`` -#: - ``'peak_absolute'`` -#: - ``'peak_signal_to_noise_ratio'`` -#: - ``'perceptual_hash'`` - Available with ImageMagick-7 -#: - ``'root_mean_square'`` -#: - ``'structural_similarity'`` - Available with ImageMagick-7 -#: - ``'structural_dissimilarity'`` - Available with ImageMagick-7 -#: -#: .. seealso:: -#: -#: `ImageMagick Compare Operations`__ -#: -#: __ http://www.imagemagick.org/Usage/compare/ -#: -#: .. versionadded:: 0.4.3 -#: -#: .. versionchanged:: 0.5.4 - Remapped :c:data:`MetricType` enum. -COMPARE_METRICS = ('undefined', 'absolute', - 'mean_absolute', 'mean_error_per_pixel', - 'mean_squared', 'peak_absolute', - 'peak_signal_to_noise_ratio', 'root_mean_square', - 'normalized_cross_correlation', 'fuzz') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - COMPARE_METRICS = ('undefined', 'absolute', 'fuzz', 'mean_absolute', - 'mean_error_per_pixel', 'mean_squared', - 'normalized_cross_correlation', 'peak_absolute', - 'peak_signal_to_noise_ratio', 'perceptual_hash', - 'root_mean_square', 'structural_similarity', - 'structural_dissimilarity') - - -#: (:class:`tuple`) The list of complex operators used by -#: :meth:`Image.complex() `. -#: -#: - ``'undefined'`` -#: - ``'add'`` -#: - ``'conjugate'`` -#: - ``'divide'`` -#: - ``'magnitude',`` -#: - ``'multiply'`` -#: - ``'real_imaginary'`` -#: - ``'subtract'`` -#: -#: .. versionadded:: 0.5.5 -COMPLEX_OPERATORS = ('undefined', 'add', 'conjugate', 'divide', 'magnitude', - 'multiply', 'real_imaginary', 'subtract') - - -#: (:class:`tuple`) The list of composition operators -#: -#: - ``'undefined'`` -#: - ``'alpha'`` - Only available with ImageMagick-7 -#: - ``'atop'`` -#: - ``'blend'`` -#: - ``'blur'`` -#: - ``'bumpmap'`` -#: - ``'change_mask'`` -#: - ``'clear'`` -#: - ``'color_burn'`` -#: - ``'color_dodge'`` -#: - ``'colorize'`` -#: - ``'copy_black'`` -#: - ``'copy_blue'`` -#: - ``'copy'`` -#: - ``'copy_alpha'`` - Only available with ImageMagick-7 -#: - ``'copy_cyan'`` -#: - ``'copy_green'`` -#: - ``'copy_magenta'`` -#: - ``'copy_opacity'`` - Only available with ImageMagick-6 -#: - ``'copy_red'`` -#: - ``'copy_yellow'`` -#: - ``'darken'`` -#: - ``'darken_intensity'`` -#: - ``'difference'`` -#: - ``'displace'`` -#: - ``'dissolve'`` -#: - ``'distort'`` -#: - ``'divide_dst'`` -#: - ``'divide_src'`` -#: - ``'dst_atop'`` -#: - ``'dst'`` -#: - ``'dst_in'`` -#: - ``'dst_out'`` -#: - ``'dst_over'`` -#: - ``'exclusion'`` -#: - ``'hard_light'`` -#: - ``'hard_mix'`` -#: - ``'hue'`` -#: - ``'in'`` -#: - ``'intensity'`` - Only available with ImageMagick-7 -#: - ``'lighten'`` -#: - ``'lighten_intensity'`` -#: - ``'linear_burn'`` -#: - ``'linear_dodge'`` -#: - ``'linear_light'`` -#: - ``'luminize'`` -#: - ``'mathematics'`` -#: - ``'minus_dst'`` -#: - ``'minus_src'`` -#: - ``'modulate'`` -#: - ``'modulus_add'`` -#: - ``'modulus_subtract'`` -#: - ``'multiply'`` -#: - ``'no'`` -#: - ``'out'`` -#: - ``'over'`` -#: - ``'overlay'`` -#: - ``'pegtop_light'`` -#: - ``'pin_light'`` -#: - ``'plus'`` -#: - ``'replace'`` -#: - ``'saturate'`` -#: - ``'screen'`` -#: - ``'soft_light'`` -#: - ``'src_atop'`` -#: - ``'src'`` -#: - ``'src_in'`` -#: - ``'src_out'`` -#: - ``'src_over'`` -#: - ``'threshold'`` -#: - ``'vivid_light'`` -#: - ``'xor'`` -#: - ``'stereo'`` -#: -#: .. versionchanged:: 0.3.0 -#: Renamed from :const:`COMPOSITE_OPS` to :const:`COMPOSITE_OPERATORS`. -#: -#: .. versionchanged:: 0.5.6 -#: Operators have been updated to reflect latest changes in C-API. -#: For ImageMagick-6, ``'add'`` has been renamed to ``'modulus_add'``, -#: ``'subtract'`` has been renamed to ``'modulus_subtract'``, -#: ``'divide'`` has been split into ``'divide_dst'`` & ``'divide_src'``, and -#: ``'minus'`` has been split into ``'minus_dst'`` & ``'minus_src'``. -#: -#: .. seealso:: -#: -#: `Compositing Images`__ ImageMagick v6 Examples -#: Image composition is the technique of combining images that have, -#: or do not have, transparency or an alpha channel. -#: This is usually performed using the IM :program:`composite` command. -#: It may also be performed as either part of a larger sequence of -#: operations or internally by other image operators. -#: -#: `ImageMagick Composition Operators`__ -#: Demonstrates the results of applying the various composition -#: composition operators. -#: -#: __ http://www.imagemagick.org/Usage/compose/ -#: __ http://www.rubblewebs.co.uk/imagemagick/operators/compose.php -COMPOSITE_OPERATORS = ( - 'undefined', 'no', 'modulus_add', 'atop', 'blend', 'bumpmap', - 'change_mask', 'clear', 'color_burn', 'color_dodge', 'colorize', - 'copy_black', 'copy_blue', 'copy', 'copy_cyan', 'copy_green', - 'copy_magenta', 'copy_opacity', 'copy_red', 'copy_yellow', 'darken', - 'dst_atop', 'dst', 'dst_in', 'dst_out', 'dst_over', 'difference', - 'displace', 'dissolve', 'exclusion', 'hard_light', 'hue', 'in', 'lighten', - 'linear_light', 'luminize', 'minus_dst', 'modulate', 'multiply', 'out', - 'over', 'overlay', 'plus', 'replace', 'saturate', 'screen', 'soft_light', - 'src_atop', 'src', 'src_in', 'src_out', 'src_over', 'modulus_subtract', - 'threshold', 'xor', 'divide_dst', 'distort', 'blur', 'pegtop_light', - 'vivid_light', 'pin_light', 'linear_dodge', 'linear_burn', 'mathematics', - 'divide_src', 'minus_src', 'darken_intensity', 'lighten_intensity', - 'hard_mix', 'stereo' -) -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - COMPOSITE_OPERATORS = ( - 'undefined', 'alpha', 'atop', 'blend', 'blur', 'bumpmap', - 'change_mask', 'clear', 'color_burn', 'color_dodge', 'colorize', - 'copy_black', 'copy_blue', 'copy', 'copy_cyan', 'copy_green', - 'copy_magenta', 'copy_alpha', 'copy_red', 'copy_yellow', 'darken', - 'darken_intensity', 'difference', 'displace', 'dissolve', 'distort', - 'divide_dst', 'divide_src', 'dst_atop', 'dst', 'dst_in', 'dst_out', - 'dst_over', 'exclusion', 'hard_light', 'hard_mix', 'hue', 'in', - 'intensity', 'lighten', 'lighten_intensity', 'linear_burn', - 'linear_dodge', 'linear_light', 'luminize', 'mathematics', 'minus_dst', - 'minus_src', 'modulate', 'modulus_add', 'modulus_subtract', 'multiply', - 'no', 'out', 'over', 'overlay', 'pegtop_light', 'pin_light', 'plus', - 'replace', 'saturate', 'screen', 'soft_light', 'src_atop', 'src', - 'src_in', 'src_out', 'src_over', 'threshold', 'vivid_light', 'xor', - 'stereo' - ) - -#: (:class:`tuple`) The list of :attr:`Image.compression` types. -#: -#: - ``'undefined'`` -#: - ``'b44a'`` -#: - ``'b44'`` -#: - ``'bzip'`` -#: - ``'dxt1'`` -#: - ``'dxt3'`` -#: - ``'dxt5'`` -#: - ``'fax'`` -#: - ``'group4'`` -#: - ``'jbig1'`` -#: - ``'jbig2'`` -#: - ``'jpeg2000'`` -#: - ``'jpeg'`` -#: - ``'losslessjpeg'`` -#: - ``'lzma'`` -#: - ``'lzw'`` -#: - ``'no'`` -#: - ``'piz'`` -#: - ``'pxr24'`` -#: - ``'rle'`` -#: - ``'zip'`` -#: - ``'zips'`` -#: -#: .. versionadded:: 0.3.6 -#: .. versionchanged:: 0.5.0 -#: Support for ImageMagick-6 & ImageMagick-7 -COMPRESSION_TYPES = ( - 'undefined', 'no', 'bzip', 'dxt1', 'dxt3', 'dxt5', - 'fax', 'group4', 'jpeg', 'jpeg2000', 'losslessjpeg', - 'lzw', 'rle', 'zip', 'zips', 'piz', 'pxr24', 'b44', - 'b44a', 'lzma', 'jbig1', 'jbig2' -) -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - COMPRESSION_TYPES = ( - 'undefined', 'b44a', 'b44', 'bzip', 'dxt1', 'dxt3', 'dxt5', 'fax', - 'group4', 'jbig1', 'jbig2', 'jpeg2000', 'jpeg', 'losslessjpeg', - 'lzma', 'lzw', 'no', 'piz', 'pxr24', 'rle', 'zip', 'zips' - ) - -#: (:class:`tuple`) The list of :attr:`BaseImage.dispose` types. -#: -#: - ``'undefined'`` -#: - ``'none'`` -#: - ``'background'`` -#: - ``'previous'`` -#: -#: .. versionadded:: 0.5.0 -DISPOSE_TYPES = ( - 'undefined', - 'none', - 'background', - 'previous' -) - - -#: (:class:`tuple`) The list of :meth:`BaseImage.distort` methods. -#: -#: - ``'undefined'`` -#: - ``'affine'`` -#: - ``'affine_projection'`` -#: - ``'scale_rotate_translate'`` -#: - ``'perspective'`` -#: - ``'perspective_projection'`` -#: - ``'bilinear_forward'`` -#: - ``'bilinear_reverse'`` -#: - ``'polynomial'`` -#: - ``'arc'`` -#: - ``'polar'`` -#: - ``'depolar'`` -#: - ``'cylinder_2_plane'`` -#: - ``'plane_2_cylinder'`` -#: - ``'barrel'`` -#: - ``'barrel_inverse'`` -#: - ``'shepards'`` -#: - ``'resize'`` -#: - ``'sentinel'`` -#: - ``'rigidaffine'`` - Only available with ImageMagick-7.0.10, or later. -#: -#: .. versionadded:: 0.4.1 -DISTORTION_METHODS = ( - 'undefined', 'affine', 'affine_projection', 'scale_rotate_translate', - 'perspective', 'perspective_projection', 'bilinear_forward', - 'bilinear_reverse', 'polynomial', 'arc', 'polar', 'depolar', - 'cylinder_2_plane', 'plane_2_cylinder', 'barrel', 'barrel_inverse', - 'shepards', 'resize', 'sentinel', 'rigidaffine' -) - - -#: (:class:`tuple`) The list of Dither methods. Used by -#: :meth:`Image.posterize() ` and -#: :meth:`Image.remap() ` methods. -#: -#: - ``'undefined'`` -#: - ``'no'`` -#: - ``'riemersma'`` -#: - ``'floyd_steinberg'`` -#: -#: .. versionadded:: 0.5.0 -DITHER_METHODS = ('undefined', 'no', 'riemersma', 'floyd_steinberg') - - -#: (:class:`tuple`) The list of evaluation operators. Used by -#: :meth:`Image.evaluate() ` method. -#: -#: - ``'undefined'`` -#: - ``'abs'`` -#: - ``'add'`` -#: - ``'addmodulus'`` -#: - ``'and'`` -#: - ``'cosine'`` -#: - ``'divide'`` -#: - ``'exponential'`` -#: - ``'gaussiannoise'`` -#: - ``'impulsenoise'`` -#: - ``'inverse_log'`` - Added with ImageMagick-7.0.10-24 -#: - ``'laplaciannoise'`` -#: - ``'leftshift'`` -#: - ``'log'`` -#: - ``'max'`` -#: - ``'mean'`` -#: - ``'median'`` -#: - ``'min'`` -#: - ``'multiplicativenoise'`` -#: - ``'multiply'`` -#: - ``'or'`` -#: - ``'poissonnoise'`` -#: - ``'pow'`` -#: - ``'rightshift'`` -#: - ``'set'`` -#: - ``'sine'`` -#: - ``'subtract'`` -#: - ``'sum'`` -#: - ``'threshold'`` -#: - ``'thresholdblack'`` -#: - ``'thresholdwhite'`` -#: - ``'uniformnoise'`` -#: - ``'xor'`` -#: -#: .. seealso:: -#: -#: `ImageMagick Image Evaluation Operators`__ -#: Describes the MagickEvaluateImageChannel method and lists the -#: various evaluations operators -#: -#: __ http://www.magickwand.org/MagickEvaluateImage.html -EVALUATE_OPS = ('undefined', 'add', 'and', 'divide', 'leftshift', 'max', - 'min', 'multiply', 'or', 'rightshift', 'set', 'subtract', - 'xor', 'pow', 'log', 'threshold', 'thresholdblack', - 'thresholdwhite', 'gaussiannoise', 'impulsenoise', - 'laplaciannoise', 'multiplicativenoise', 'poissonnoise', - 'uniformnoise', 'cosine', 'sine', 'addmodulus', 'mean', - 'abs', 'exponential', 'median', 'sum', 'rootmeansquare') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - EVALUATE_OPS = ('undefined', 'abs', 'add', 'addmodulus', 'and', 'cosine', - 'divide', 'exponential', 'gaussiannoise', 'impulsenoise', - 'laplaciannoise', 'leftshift', 'log', 'max', 'mean', - 'median', 'min', 'multiplicativenoise', 'multiply', 'or', - 'poissonnoise', 'pow', 'rightshift', 'rootmeansquare', - 'set', 'sine', 'subtract', 'sum', 'thresholdblack', - 'threshold', 'thresholdwhite', 'uniformnoise', 'xor', - 'inverse_log') - - -#: (:class:`tuple`) The list of filter types. Used by -#: :meth:`Image.resample() ` and -#: :meth:`Image.resize() ` methods. -#: -#: - ``'undefined'`` -#: - ``'point'`` -#: - ``'box'`` -#: - ``'triangle'`` -#: - ``'hermite'`` -#: - ``'hanning'`` -#: - ``'hamming'`` -#: - ``'blackman'`` -#: - ``'gaussian'`` -#: - ``'quadratic'`` -#: - ``'cubic'`` -#: - ``'catrom'`` -#: - ``'mitchell'`` -#: - ``'jinc'`` -#: - ``'sinc'`` -#: - ``'sincfast'`` -#: - ``'kaiser'`` -#: - ``'welsh'`` -#: - ``'parzen'`` -#: - ``'bohman'`` -#: - ``'bartlett'`` -#: - ``'lagrange'`` -#: - ``'lanczos'`` -#: - ``'lanczossharp'`` -#: - ``'lanczos2'`` -#: - ``'lanczos2sharp'`` -#: - ``'robidoux'`` -#: - ``'robidouxsharp'`` -#: - ``'cosine'`` -#: - ``'spline'`` -#: - ``'sentinel'`` -#: -#: .. seealso:: -#: -#: `ImageMagick Resize Filters`__ -#: Demonstrates the results of resampling images using the various -#: resize filters and blur settings available in ImageMagick. -#: -#: __ http://www.imagemagick.org/Usage/resize/ -FILTER_TYPES = ('undefined', 'point', 'box', 'triangle', 'hermite', 'hanning', - 'hamming', 'blackman', 'gaussian', 'quadratic', 'cubic', - 'catrom', 'mitchell', 'jinc', 'sinc', 'sincfast', 'kaiser', - 'welsh', 'parzen', 'bohman', 'bartlett', 'lagrange', 'lanczos', - 'lanczossharp', 'lanczos2', 'lanczos2sharp', 'robidoux', - 'robidouxsharp', 'cosine', 'spline', 'sentinel') - - -#: (:class:`tuple`) The list of :attr:`Image.function ` -#: types. -#: -#: - ``'undefined'`` -#: - ``'arcsin'`` -#: - ``'arctan'`` -#: - ``'polynomial'`` -#: - ``'sinusoid'`` -FUNCTION_TYPES = ('undefined', 'polynomial', 'sinusoid', 'arcsin', 'arctan') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - FUNCTION_TYPES = ('undefined', 'arcsin', 'arctan', 'polynomial', - 'sinusoid') - - -#: (:class:`tuple`) The list of :attr:`~BaseImage.gravity` types. -#: -#: - ``'forget'`` -#: - ``'north_west'`` -#: - ``'north'`` -#: - ``'north_east'`` -#: - ``'west'`` -#: - ``'center'`` -#: - ``'east'`` -#: - ``'south_west'`` -#: - ``'south'`` -#: - ``'south_east'`` -#: -#: .. versionadded:: 0.3.0 -GRAVITY_TYPES = ('forget', 'north_west', 'north', 'north_east', 'west', - 'center', 'east', 'south_west', 'south', 'south_east', - 'static') - - -#: (:class:`tuple`) The list of methods for :meth:`~BaseImage.merge_layers` -#: and :meth:`~Image.compare_layers`. -#: -#: - ``'undefined'`` -#: - ``'coalesce'`` -#: - ``'compareany'`` - Only used for :meth:`~Image.compare_layers`. -#: - ``'compareclear'`` - Only used for :meth:`~Image.compare_layers`. -#: - ``'compareoverlay'`` - Only used for :meth:`~Image.compare_layers`. -#: - ``'dispose'`` -#: - ``'optimize'`` -#: - ``'optimizeimage'`` -#: - ``'optimizeplus'`` -#: - ``'optimizetrans'`` -#: - ``'removedups'`` -#: - ``'removezero'`` -#: - ``'composite'`` -#: - ``'merge'`` - Only used for :meth:`~BaseImage.merge_layers`. -#: - ``'flatten'`` - Only used for :meth:`~BaseImage.merge_layers`. -#: - ``'mosaic'`` - Only used for :meth:`~BaseImage.merge_layers`. -#: - ``'trimbounds'`` - Only used for :meth:`~BaseImage.merge_layers`. -#: -#: .. versionadded:: 0.4.3 -IMAGE_LAYER_METHOD = ('undefined', 'coalesce', 'compareany', 'compareclear', - 'compareoverlay', 'dispose', 'optimize', 'optimizeimage', - 'optimizeplus', 'optimizetrans', 'removedups', - 'removezero', 'composite', 'merge', 'flatten', 'mosaic', - 'trimbounds') - - -#: (:class:`tuple`) The list of image types -#: -#: - ``'undefined'`` -#: - ``'bilevel'`` -#: - ``'grayscale'`` -#: - ``'grayscalealpha'`` - Only available with ImageMagick-7 -#: - ``'grayscalematte'`` - Only available with ImageMagick-6 -#: - ``'palette'`` -#: - ``'palettealpha'`` - Only available with ImageMagick-7 -#: - ``'palettematte'`` - Only available with ImageMagick-6 -#: - ``'truecolor'`` -#: - ``'truecoloralpha'`` - Only available with ImageMagick-7 -#: - ``'truecolormatte'`` - Only available with ImageMagick-6 -#: - ``'colorseparation'`` -#: - ``'colorseparationalpha'`` - Only available with ImageMagick-7 -#: - ``'colorseparationmatte'`` - Only available with ImageMagick-6 -#: - ``'optimize'`` -#: - ``'palettebilevelalpha'`` - Only available with ImageMagick-7 -#: - ``'palettebilevelmatte'`` - Only available with ImageMagick-6 -#: -#: .. seealso:: -#: -#: `ImageMagick Image Types`__ -#: Describes the MagickSetImageType method which can be used -#: to set the type of an image -#: -#: __ http://www.imagemagick.org/api/magick-image.php#MagickSetImageType -IMAGE_TYPES = ('undefined', 'bilevel', 'grayscale', 'grayscalematte', - 'palette', 'palettematte', 'truecolor', 'truecolormatte', - 'colorseparation', 'colorseparationmatte', 'optimize', - 'palettebilevelmatte') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - IMAGE_TYPES = ('undefined', 'bilevel', 'grayscale', 'grayscalealpha', - 'palette', 'palettealpha', 'truecolor', 'truecoloralpha', - 'colorseparation', 'colorseparationalpha', 'optimize', - 'palettebilevelalpha') - - -#: (:class:`tuple`) The list of interlace schemes. -#: -#: - ``'undefined'`` -#: - ``'no'`` -#: - ``'line'`` -#: - ``'plane'`` -#: - ``'partition'`` -#: - ``'gif'`` -#: - ``'jpeg'`` -#: - ``'png'`` -#: -#: .. versionadded:: 0.5.2 -INTERLACE_TYPES = ('undefined', 'no', 'line', 'plane', 'partition', 'gif', - 'jpeg', 'png') - - -#: (:class:`tuple`) The list of builtin kernels. -#: -#: - ``'undefined'`` -#: - ``'unity'`` -#: - ``'gaussian'`` -#: - ``'dog'`` -#: - ``'log'`` -#: - ``'blur'`` -#: - ``'comet'`` -#: - ``'laplacian'`` -#: - ``'sobel'`` -#: - ``'frei_chen'`` -#: - ``'roberts'`` -#: - ``'prewitt'`` -#: - ``'compass'`` -#: - ``'kirsch'`` -#: - ``'diamond'`` -#: - ``'square'`` -#: - ``'rectangle'`` -#: - ``'octagon'`` -#: - ``'disk'`` -#: - ``'plus'`` -#: - ``'cross'`` -#: - ``'ring'`` -#: - ``'peaks'`` -#: - ``'edges'`` -#: - ``'corners'`` -#: - ``'diagonals'`` -#: - ``'line_ends'`` -#: - ``'line_junctions'`` -#: - ``'ridges'`` -#: - ``'convex_hull'`` -#: - ``'thin_se'`` -#: - ``'skeleton'`` -#: - ``'chebyshev'`` -#: - ``'manhattan'`` -#: - ``'octagonal'`` -#: - ``'euclidean'`` -#: - ``'user_defined'`` -#: - ``'binomial'`` -#: -KERNEL_INFO_TYPES = ('undefined', 'unity', 'gaussian', 'dog', 'log', 'blur', - 'comet', 'laplacian', 'sobel', 'frei_chen', 'roberts', - 'prewitt', 'compass', 'kirsch', 'diamond', 'square', - 'rectangle', 'octagon', 'disk', 'plus', 'cross', 'ring', - 'peaks', 'edges', 'corners', 'diagonals', 'line_ends', - 'line_junctions', 'ridges', 'convex_hull', 'thin_se', - 'skeleton', 'chebyshev', 'manhattan', 'octagonal', - 'euclidean', 'user_defined', 'binomial') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - KERNEL_INFO_TYPES = ('undefined', 'unity', 'gaussian', 'dog', 'log', - 'blur', 'comet', 'binomial', 'laplacian', 'sobel', - 'frei_chen', 'roberts', 'prewitt', 'compass', - 'kirsch', 'diamond', 'square', 'rectangle', 'octagon', - 'disk', 'plus', 'cross', 'ring', 'peaks', 'edges', - 'corners', 'diagonals', 'line_ends', 'line_junctions', - 'ridges', 'convex_hull', 'thin_se', 'skeleton', - 'chebyshev', 'manhattan', 'octagonal', 'euclidean', - 'user_defined') - -#: (:class:`tuple`) The list of morphology methods. -#: -#: - ``'undefined'`` -#: - ``'convolve'`` -#: - ``'correlate'`` -#: - ``'erode'`` -#: - ``'dilate'`` -#: - ``'erode_intensity'`` -#: - ``'dilate_intensity'`` -#: - ``'distance'`` -#: - ``'open'`` -#: - ``'close'`` -#: - ``'open_intensity'`` -#: - ``'close_intensity'`` -#: - ``'smooth'`` -#: - ``'edgein'`` -#: - ``'edgeout'`` -#: - ``'edge'`` -#: - ``'tophat'`` -#: - ``'bottom_hat'`` -#: - ``'hit_and_miss'`` -#: - ``'thinning'`` -#: - ``'thicken'`` -#: - ``'voronoi'`` -#: - ``'iterative_distance'`` -#: -MORPHOLOGY_METHODS = ('undefined', 'convolve', 'correlate', 'erode', 'dilate', - 'erode_intensity', 'dilate_intensity', 'distance', - 'open', 'close', 'open_intensity', 'close_intensity', - 'smooth', 'edgein', 'edgeout', 'edge', 'tophat', - 'bottom_hat', 'hit_and_miss', 'thinning', 'thicken', - 'voronoi', 'iterative_distance') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - MORPHOLOGY_METHODS = ('undefined', 'convolve', 'correlate', 'erode', - 'dilate', 'erode_intensity', 'dilate_intensity', - 'iterative_distance', 'open', 'close', - 'open_intensity', 'close_intensity', 'smooth', - 'edgein', 'edgeout', 'edge', 'tophat', 'bottom_hat', - 'hit_and_miss', 'thinning', 'thicken', 'distance', - 'voronoi') - - -#: (:class:`tuple`) The list of montage behaviors used by -#: :meth:`Image.montage()` method. -#: -#: - ``'undefined'`` -#: - ``'frame'`` -#: - ``'unframe'`` -#: - ``'concatenate'`` -#: -#: .. versionadded:: 0.6.8 -MONTAGE_MODES = ('undefined', 'frame', 'unframe', 'concatenate') - - -#: (:class:`tuple`) The list of noise types used by -#: :meth:`Image.noise() ` method. -#: -#: - ``'undefined'`` -#: - ``'uniform'`` -#: - ``'gaussian'`` -#: - ``'multiplicative_gaussian'`` -#: - ``'impulse'`` -#: - ``'laplacian'`` -#: - ``'poisson'`` -#: - ``'random'`` -#: -#: .. versionadded:: 0.5.3 -NOISE_TYPES = ('undefined', 'uniform', 'gaussian', 'multiplicative_gaussian', - 'impulse', 'laplacian', 'poisson', 'random') - - -#: (:class:`collections.abc.Set`) The set of available -#: :attr:`~BaseImage.options`. -#: -#: .. versionadded:: 0.3.0 -#: -#: .. versionchanged:: 0.3.4 -#: Added ``'jpeg:sampling-factor'`` option. -#: -#: .. versionchanged:: 0.3.9 -#: Added ``'pdf:use-cropbox'`` option. Ensure you set this option *before* -#: reading the PDF document. -#: -#: .. deprecated:: 0.5.0 -#: Any arbitrary key can be set to the option table. Key-Value pairs set -#: on the MagickWand stack allowing for various coders, kernels, morphology -#: (&tc) to pick and choose additional user-supplied properties/artifacts. -OPTIONS = frozenset([ - 'caption', - 'comment', - 'date:create', - 'date:modify', - 'exif:ColorSpace', - 'exif:InteroperabilityIndex', - 'fill', - 'film-gamma', - 'gamma', - 'hdr:exposure', - 'jpeg:colorspace', - 'jpeg:sampling-factor', - 'label', - 'pdf:use-cropbox', - 'png:bit-depth-written', - 'png:IHDR.bit-depth-orig', - 'png:IHDR.color-type-orig', - 'png:tIME', - 'reference-black', - 'reference-white', - 'signature', - 'tiff:Orientation', - 'tiff:photometric', - 'tiff:ResolutionUnit', - 'type:hinting', - 'vips:metadata' -]) - - -#: (:class:`tuple`) The list of :attr:`~BaseImage.orientation` types. -#: -#: .. versionadded:: 0.3.0 -ORIENTATION_TYPES = ('undefined', 'top_left', 'top_right', 'bottom_right', - 'bottom_left', 'left_top', 'right_top', 'right_bottom', - 'left_bottom') - - -#: (:class:`dict`) Map of papersize names to page sizes. Each page size -#: is a width & height :class:`tuple` at a 72dpi resolution. -#: -#: .. code:: -#: -#: from wand.image import Image, PAPERSIZE_MAP -#: -#: w, h = PAPERSIZE_MAP["a4"] -#: with Image(width=w, height=h, background="white") as img: -#: img.save(filename="a4_page.png") -#: -#: .. versionadded:: 0.6.4 -PAPERSIZE_MAP = { - '4x6': (288, 432), '5x7': (360, 504), '7x9': (504, 648), - '8x10': (576, 720), '9x11': (648, 792), '9x12': (648, 864), - '10x13': (720, 936), '10x14': (720, 1008), '11x17': (792, 1224), - '4A0': (4768, 6741), '2A0': (3370, 4768), 'a0': (2384, 3370), - 'a1': (1684, 2384), 'a2': (1191, 1684), 'a3': (842, 1191), - 'a4': (595, 842), 'a4small': (595, 842), 'a5': (420, 595), - 'a6': (298, 420), 'a7': (210, 298), 'a8': (147, 210), 'a9': (105, 147), - 'a10': (74, 105), 'archa': (648, 864), 'archb': (864, 1296), - 'archC': (1296, 1728), 'archd': (1728, 2592), 'arche': (2592, 3456), - 'b0': (2920, 4127), 'b1': (2064, 2920), 'b10': (91, 127), - 'b2': (1460, 2064), 'b3': (1032, 1460), 'b4': (729, 1032), - 'b5': (516, 729), 'b6': (363, 516), 'b7': (258, 363), 'b8': (181, 258), - 'b9': (127, 181), 'c0': (2599, 3676), 'c1': (1837, 2599), - 'c2': (1298, 1837), 'c3': (918, 1296), 'c4': (649, 918), 'c5': (459, 649), - 'c6': (323, 459), 'c7': (230, 323), 'csheet': (1224, 1584), - 'dsheet': (1584, 2448), 'esheet': (2448, 3168), 'executive': (540, 720), - 'flsa': (612, 936), 'flse': (612, 936), 'folio': (612, 936), - 'halfletter': (396, 612), 'isob0': (2835, 4008), 'isob1': (2004, 2835), - 'isob10': (88, 125), 'isob2': (1417, 2004), 'isob3': (1001, 1417), - 'isob4': (709, 1001), 'isob5': (499, 709), 'isob6': (354, 499), - 'isob7': (249, 354), 'isob8': (176, 249), 'isob9': (125, 176), - 'jisb0': (1030, 1456), 'jisb1': (728, 1030), 'jisb2': (515, 728), - 'jisb3': (364, 515), 'jisb4': (257, 364), 'jisb5': (182, 257), - 'jisb6': (128, 182), 'ledger': (1224, 792), 'legal': (612, 1008), - 'letter': (612, 792), 'lettersmall': (612, 792), 'monarch': (279, 540), - 'quarto': (610, 780), 'statement': (396, 612), 'tabloid': (792, 1224) -} - - -#: (:class:`tuple`) List of interpolate pixel methods (ImageMagick-7 only.) -#: -#: - ``'undefined'`` -#: - ``'average'`` -#: - ``'average9'`` -#: - ``'average16'`` -#: - ``'background'`` -#: - ``'bilinear'`` -#: - ``'blend'`` -#: - ``'catrom'`` -#: - ``'integer'`` -#: - ``'mesh'`` -#: - ``'nearest'`` -#: - ``'spline'`` -#: -#: .. versionadded:: 0.5.0 -PIXEL_INTERPOLATE_METHODS = ('undefined', 'average', 'average9', 'average16', - 'background', 'bilinear', 'blend', 'catrom', - 'integer', 'mesh', 'nearest', 'spline') - - -#: (:class:`tuple`) List of rendering intent types used for -#: :attr:`Image.rendering_intent ` -#: property. -#: -#: - ``'undefined'`` -#: - ``'saturation'`` -#: - ``'perceptual'`` -#: - ``'absolute'`` -#: - ``'relative'`` -#: -#: .. versionadded:: 0.5.4 -RENDERING_INTENT_TYPES = ('undefined', 'saturation', 'perceptual', 'absolute', - 'relative') - - -#: (:class:`tuple`) List of sparse color methods used by -#: :class:`Image.sparse_color() ` -#: -#: - ``'undefined'`` -#: - ``'barycentric'`` -#: - ``'bilinear'`` -#: - ``'shepards'`` -#: - ``'voronoi'`` -#: - ``'inverse'`` -#: - ``'manhattan'`` -#: -#: .. versionadded:: 0.5.3 -SPARSE_COLOR_METHODS = dict(undefined=0, barycentric=1, bilinear=7, - shepards=16, voronoi=18, inverse=19, - manhattan=20) - - -#: (:class:`tuple`) The list of statistic types used by -#: :meth:`Image.statistic() `. -#: -#: - ``'undefined'`` -#: - ``'gradient'`` -#: - ``'maximum'`` -#: - ``'mean'`` -#: - ``'median'`` -#: - ``'minimum'`` -#: - ``'mode'`` -#: - ``'nonpeak'`` -#: - ``'root_mean_square'`` -#: - ``'standard_deviation'`` -#: -#: .. versionadded:: 0.5.3 -STATISTIC_TYPES = ('undefined', 'gradient', 'maximum', 'mean', 'median', - 'minimum', 'mode', 'nonpeak', 'standard_deviation', - 'root_mean_square') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - STATISTIC_TYPES = ('undefined', 'gradient', 'maximum', 'mean', 'median', - 'minimum', 'mode', 'nonpeak', 'root_mean_square', - 'standard_deviation') - - -#: (:class:`tuple`) The list of pixel storage types. -#: -#: - ``'undefined'`` -#: - ``'char'`` -#: - ``'double'`` -#: - ``'float'`` -#: - ``'integer'`` -#: - ``'long'`` -#: - ``'quantum'`` -#: - ``'short'`` -#: -#: .. versionadded:: 0.5.0 -STORAGE_TYPES = ('undefined', 'char', 'double', 'float', 'integer', - 'long', 'quantum', 'short') - - -#: (:class:`tuple`) The list of resolution unit types. -#: -#: - ``'undefined'`` -#: - ``'pixelsperinch'`` -#: - ``'pixelspercentimeter'`` -#: -#: .. seealso:: -#: -#: `ImageMagick Image Units`__ -#: Describes the MagickSetImageUnits method which can be used -#: to set image units of resolution -#: -#: __ http://www.imagemagick.org/api/magick-image.php#MagickSetImageUnits -UNIT_TYPES = ('undefined', 'pixelsperinch', 'pixelspercentimeter') - - -#: (:class:`tuple`) The list of :attr:`~BaseImage.virtual_pixel` types. -#: -#: - ``'undefined'`` -#: - ``'background'`` -#: - ``'constant'`` - Only available with ImageMagick-6 -#: - ``'dither'`` -#: - ``'edge'`` -#: - ``'mirror'`` -#: - ``'random'`` -#: - ``'tile'`` -#: - ``'transparent'`` -#: - ``'mask'`` -#: - ``'black'`` -#: - ``'gray'`` -#: - ``'white'`` -#: - ``'horizontal_tile'`` -#: - ``'vertical_tile'`` -#: - ``'horizontal_tile_edge'`` -#: - ``'vertical_tile_edge'`` -#: - ``'checker_tile'`` -#: -#: .. versionadded:: 0.4.1 -VIRTUAL_PIXEL_METHOD = ('undefined', 'background', 'constant', 'dither', - 'edge', 'mirror', 'random', 'tile', 'transparent', - 'mask', 'black', 'gray', 'white', 'horizontal_tile', - 'vertical_tile', 'horizontal_tile_edge', - 'vertical_tile_edge', 'checker_tile') -if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - VIRTUAL_PIXEL_METHOD = ('undefined', 'background', 'dither', - 'edge', 'mirror', 'random', 'tile', 'transparent', - 'mask', 'black', 'gray', 'white', - 'horizontal_tile', 'vertical_tile', - 'horizontal_tile_edge', 'vertical_tile_edge', - 'checker_tile') - - -def manipulative(function): - """Mark the operation manipulating itself instead of returning new one.""" - @functools.wraps(function) - def wrapped(self, *args, **kwargs): - result = function(self, *args, **kwargs) - self.dirty = True - return result - return wrapped - - -def trap_exception(function): - @functools.wraps(function) - def wrapped(self, *args, **kwargs): - result = function(self, *args, **kwargs) - if not bool(result): - self.raise_exception() - return result - return wrapped - - -class BaseImage(Resource): - """The abstract base of :class:`Image` (container) and - :class:`~wand.sequence.SingleImage`. That means the most of - operations, defined in this abstract class, are possible for - both :class:`Image` and :class:`~wand.sequence.SingleImage`. - - .. versionadded:: 0.3.0 - - """ - - #: (:class:`OptionDict`) The mapping of internal option settings. - #: - #: .. versionadded:: 0.3.0 - #: - #: .. versionchanged:: 0.3.4 - #: Added ``'jpeg:sampling-factor'`` option. - #: - #: .. versionchanged:: 0.3.9 - #: Added ``'pdf:use-cropbox'`` option. - options = None - - #: (:class:`collections.abc.Sequence`) The list of - #: :class:`~wand.sequence.SingleImage`\ s that the image contains. - #: - #: .. versionadded:: 0.3.0 - sequence = None - - #: (:class:`bool`) Whether the image is changed or not. - dirty = None - - #: (:class:`numbers.Integral`) Internal placeholder for - #: :attr:`seed` property. - #: - #: .. versionadded:: 0.5.5 - _seed = None - - c_is_resource = library.IsMagickWand - c_destroy_resource = library.DestroyMagickWand - c_get_exception = library.MagickGetException - c_clear_exception = library.MagickClearException - - __slots__ = '_wand', - - def __init__(self, wand): - self.wand = wand - self.channel_images = ChannelImageDict(self) - self.channel_depths = ChannelDepthDict(self) - self.options = OptionDict(self) - self.dirty = False - - def __eq__(self, other): - if isinstance(other, type(self)): - return self.signature == other.signature - return False - - def __getitem__(self, idx): - if (not isinstance(idx, string_type) and - isinstance(idx, abc.Iterable)): - idx = tuple(idx) - d = len(idx) - if not (1 <= d <= 2): - raise ValueError('index cannot be {0}-dimensional'.format(d)) - elif d == 2: - x, y = idx - x_slice = isinstance(x, slice) - y_slice = isinstance(y, slice) - if x_slice and not y_slice: - y = slice(y, y + 1) - elif not x_slice and y_slice: - x = slice(x, x + 1) - elif not (x_slice or y_slice): - if not (isinstance(x, numbers.Integral) and - isinstance(y, numbers.Integral)): - raise TypeError('x and y must be integral, not ' + - repr((x, y))) - if x < 0: - x += self.width - if y < 0: - y += self.height - if x >= self.width: - raise IndexError('x must be less than width') - elif y >= self.height: - raise IndexError('y must be less than height') - elif x < 0: - raise IndexError('x cannot be less than 0') - elif y < 0: - raise IndexError('y cannot be less than 0') - with iter(self) as iterator: - iterator.seek(y) - return iterator.next(x) - if not (x.step is None and y.step is None): - raise ValueError('slicing with step is unsupported') - elif (x.start is None and x.stop is None and - y.start is None and y.stop is None): - return self.clone() - cloned = self.clone() - try: - cloned.crop(x.start, y.start, x.stop, y.stop) - except ValueError as e: - raise IndexError(str(e)) - return cloned - else: - return self[idx[0]] - elif isinstance(idx, numbers.Integral): - if idx < 0: - idx += self.height - elif idx >= self.height: - raise IndexError('index must be less than height, but got ' + - repr(idx)) - elif idx < 0: - raise IndexError('index cannot be less than zero, but got ' + - repr(idx)) - with iter(self) as iterator: - iterator.seek(idx) - return iterator.next() - elif isinstance(idx, slice): - return self[:, idx] - raise TypeError('unsupported index type: ' + repr(idx)) - - def __setitem__(self, idx, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - if not isinstance(idx, abc.Iterable): - raise TypeError('Expecting list of x,y coordinates, not ' + - repr(idx)) - idx = tuple(idx) - if len(idx) != 2: - msg = 'pixel index can not be {0}-dimensional'.format(len(idx)) - raise ValueError(msg) - colorspace = self.colorspace - s_index = STORAGE_TYPES.index("double") - width, height = self.size - x1, y1 = idx - x2, y2 = 1, 1 - if not (isinstance(x1, numbers.Integral) and - isinstance(y1, numbers.Integral)): - raise TypeError('Expecting x & y to be integers') - if x1 < 0: - x1 += width - if y1 < 0: - y1 += height - if x1 >= width: - raise ValueError('x must be less than image width') - elif y1 >= height: - raise ValueError('y must be less than image height') - if colorspace == 'gray': - channel_map = b'I' - pixel = (ctypes.c_double * 1)() - pixel[0] = color.red - elif colorspace == 'cmyk': - channel_map = b'CMYK' - pixel = (ctypes.c_double * 5)() - pixel[0] = color.red - pixel[1] = color.green - pixel[2] = color.blue - pixel[3] = color.black - if self.alpha_channel: - channel_map += b'A' - pixel[4] = color.alpha - else: - channel_map = b'RGB' - pixel = (ctypes.c_double * 4)() - pixel[0] = color.red - pixel[1] = color.green - pixel[2] = color.blue - if self.alpha_channel: - channel_map += b'A' - pixel[3] = color.alpha - r = library.MagickImportImagePixels(self.wand, - x1, y1, x2, y2, - channel_map, - s_index, - ctypes.byref(pixel)) - if not r: - self.raise_exception() - - def __hash__(self): - return hash(self.signature) - - def __iter__(self): - return Iterator(image=self) - - def __len__(self): - return self.height - - def __ne__(self, other): - return not (self == other) - - def __repr__(self, extra_format=' ({self.width}x{self.height})'): - cls = type(self) - typename = '{0}.{1}'.format( - cls.__module__, - getattr(cls, '__qualname__', cls.__name__) - ) - if getattr(self, 'c_resource', None) is None: - return '<{0}: (closed)>'.format(typename) - sig = self.signature - if not sig: - return '<{0}: (empty)>'.format(typename) - return '<{0}: {1}{2}>'.format( - typename, sig[:7], extra_format.format(self=self) - ) - - @property - def __array_interface__(self): - """Allows image-data from :class:`Image ` - instances to be loaded into numpy's array. - - .. code:: - - import numpy - from wand.image import Image - - with Image(filename='rose:') as img: - img_data = numpy.asarray(img) - - :raises ValueError: if image has no data. - - .. versionadded:: 0.5.0 - .. versionchanged:: 0.6.0 - The :attr:`shape` property is now ordered by ``height``, ``width``, - and ``channel``. - .. versionchanged:: 0.6.2 - Color spaces ``gray`` & ``cmyk`` are now supported. - """ - if not self.signature: - raise ValueError("No image data to interface with.") - width, height = self.size - storage_type = 1 # CharPixel - cs = self.colorspace - if cs in ('gray',): - channel_format = binary('R') - elif cs in ('cmyk',): - channel_format = binary('CMYK') - else: - channel_format = binary('RGB') - if self.alpha_channel: - channel_format += binary('A') - channel_number = len(channel_format) - self._c_buffer = (width * height * channel_number * ctypes.c_char)() - # FIXME: Move to pixel-data import/export methods. - r = library.MagickExportImagePixels(self.wand, - 0, 0, width, height, - channel_format, storage_type, - ctypes.byref(self._c_buffer)) - if not r: - self.raise_exception() - return dict(data=(ctypes.addressof(self._c_buffer), True), - shape=(height, width, channel_number), - typestr='|u1', - version=3) - - @property - def alpha_channel(self): - """(:class:`bool`) Get state of image alpha channel. - It can also be used to enable/disable alpha channel, but with different - behavior such as new, copied, or existing. - - Behavior of setting :attr:`alpha_channel` is defined with the - following values: - - - ``'activate'``, ``'on'``, or :const:`True` will enable an images - alpha channel. Existing alpha data is preserved. - - ``'deactivate'``, ``'off'``, or :const:`False` will disable an images - alpha channel. Any data on the alpha will be preserved. - - ``'associate'`` & ``'disassociate'`` toggle alpha channel flag in - certain image-file specifications. - - ``'set'`` enables and resets any data in an images alpha channel. - - ``'opaque'`` enables alpha/matte channel, and forces full opaque - image. - - ``'transparent'`` enables alpha/matte channel, and forces full - transparent image. - - ``'extract'`` copies data in alpha channel across all other channels, - and disables alpha channel. - - ``'copy'`` calculates the gray-scale of RGB channels, - and applies it to alpha channel. - - ``'shape'`` is identical to ``'copy'``, but will color the resulting - image with the value defined with :attr:`background_color`. - - ``'remove'`` will composite :attr:`background_color` value. - - ``'background'`` replaces full-transparent color with background - color. - - .. note:: - - The :attr:`alpha_channel` attribute will always return ``True`` - if alpha channel is enabled, and ``False`` otherwise. Setting - this property with a string value from :const:`ALPHA_CHANNEL_TYPES` - will resolve to a :class:`bool` after applying channel operations - listed above. - - With ImageMagick-6, values ``'on'`` & ``'off'`` are aliased to - ``'activate'`` & ``'deactivate'``. However in ImageMagick-7, - both ``'on'`` & ``'off'`` have their own behavior. - - - .. versionadded:: 0.2.1 - - .. versionchanged:: 0.4.1 - Support for additional setting values. - However :attr:`Image.alpha_channel` will continue to return - :class:`bool` if the current alpha/matte state is enabled. - - .. versionchanged:: 0.6.0 - Setting the alpha channel will apply the change to all frames - in the image stack. - """ - return bool(library.MagickGetImageAlphaChannel(self.wand)) - - @alpha_channel.setter - @manipulative - def alpha_channel(self, alpha_type): - is_im6 = MAGICK_VERSION_NUMBER < 0x700 - # Map common aliases for ``'deactivate'`` - if alpha_type is False or (alpha_type == 'off' and is_im6): - alpha_type = 'deactivate' - # Map common aliases for ``'activate'`` - elif alpha_type is True or (alpha_type == 'on' and is_im6): - alpha_type = 'activate' - assertions.string_in_list(ALPHA_CHANNEL_TYPES, - 'wand.image.ALPHA_CHANNEL_TYPES', - alpha_channel=alpha_type) - alpha_index = ALPHA_CHANNEL_TYPES.index(alpha_type) - library.MagickSetLastIterator(self.wand) - n = library.MagickGetIteratorIndex(self.wand) - library.MagickResetIterator(self.wand) - for i in xrange(0, n + 1): - library.MagickSetIteratorIndex(self.wand, i) - library.MagickSetImageAlphaChannel(self.wand, alpha_index) - - @property - def animation(self): - """(:class:`bool`) Whether the image is animation or not. - It doesn't only mean that the image has two or more images (frames), - but all frames are even the same size. It's about image format, - not content. It's :const:`False` even if :mimetype:`image/ico` - consits of two or more images of the same size. - - For example, it's :const:`False` for :mimetype:`image/jpeg`, - :mimetype:`image/gif`, :mimetype:`image/ico`. - - If :mimetype:`image/gif` has two or more frames, it's :const:`True`. - If :mimetype:`image/gif` has only one frame, it's :const:`False`. - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.3.8 - Became to accept :mimetype:`image/x-gif` as well. - - """ - return False - - @property - def antialias(self): - """(:class:`bool`) If vectors & fonts will use anti-aliasing. - - .. versionchanged:: 0.5.0 - Previously named :attr:`font_antialias`. - """ - return bool(library.MagickGetAntialias(self.wand)) - - @antialias.setter - @manipulative - def antialias(self, antialias): - assertions.assert_bool(antialias=antialias) - library.MagickSetAntialias(self.wand, antialias) - - @property - def background_color(self): - """(:class:`wand.color.Color`) The image background color. - It can also be set to change the background color. - - .. versionadded:: 0.1.9 - - .. versionchanged:: 0.6.7 - Allow property to be set before image read. - """ - pixel = library.NewPixelWand() - if library.MagickGetNumberImages(self.wand): - result = library.MagickGetImageBackgroundColor(self.wand, pixel) - else: - pixel = library.MagickGetBackgroundColor(self.wand) - result = True - if not result: # pragma: no cover - self.raise_exception() - else: - color = Color.from_pixelwand(pixel) - pixel = library.DestroyPixelWand(pixel) - return color - - @background_color.setter - @manipulative - def background_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - with color: - # Only set the image background if an image was loaded. - if library.MagickGetNumberImages(self.wand): - result = library.MagickSetImageBackgroundColor(self.wand, - color.resource) - if not result: # pragma: no cover - self.raise_exception() - # Also set the image stack. - result = library.MagickSetBackgroundColor(self.wand, - color.resource) - if not result: - self.raise_exception() - - @property - def blue_primary(self): - """(:class:`tuple`) The chromatic blue primary point for the image. - With ImageMagick-6 the primary value is ``(x, y)`` coordinates; - however, ImageMagick-7 has ``(x, y, z)``. - - .. versionadded:: 0.5.2 - """ - x = ctypes.c_double(0.0) - y = ctypes.c_double(0.0) - r = None - p = None - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickGetImageBluePrimary(self.wand, x, y) - p = (x.value, y.value) - else: # pragma: no cover - z = ctypes.c_double(0.0) - r = library.MagickGetImageBluePrimary(self.wand, x, y, z) - p = (x.value, y.value, z.value) - if not r: # pragma: no cover - self.raise_exception() - return p - - @blue_primary.setter - def blue_primary(self, coordinates): - r = None - if not isinstance(coordinates, abc.Sequence): - raise TypeError('Primary must be a tuple') - if MAGICK_VERSION_NUMBER < 0x700: - x, y = coordinates - r = library.MagickSetImageBluePrimary(self.wand, x, y) - else: # pragma: no cover - x, y, z = coordinates - r = library.MagickSetImageBluePrimary(self.wand, x, y, z) - if not r: # pragma: no cover - self.raise_exception() - - @property - def border_color(self): - """(:class:`wand.color.Color`) The image border color. Used for - special effects like :meth:`polaroid()`. - - .. versionadded:: 0.5.4 - """ - pixel = library.NewPixelWand() - result = library.MagickGetImageBorderColor(self.wand, pixel) - if not result: # pragma: no cover - self.raise_exception() - else: - color = Color.from_pixelwand(pixel) - pixel = library.DestroyPixelWand(pixel) - return color - - @border_color.setter - def border_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(border_color=color) - with color: - r = library.MagickSetImageBorderColor(self.wand, color.resource) - if not r: # pragma: no cover - self.raise_exception() - - @property - def colors(self): - """(:class:`numbers.Integral`) Count of unique colors used within the - image. This is READ ONLY property. - - .. versionadded:: 0.5.3 - """ - return library.MagickGetImageColors(self.wand) - - @property - def colorspace(self): - """(:class:`basestring`) The image colorspace. - - Defines image colorspace as in :const:`COLORSPACE_TYPES` enumeration. - - It may raise :exc:`ValueError` when the colorspace is unknown. - - .. versionadded:: 0.3.4 - - """ - colorspace_type_index = library.MagickGetImageColorspace(self.wand) - if not colorspace_type_index: # pragma: no cover - self.raise_exception() - return COLORSPACE_TYPES[text(colorspace_type_index)] - - @colorspace.setter - @manipulative - def colorspace(self, colorspace_type): - assertions.string_in_list(COLORSPACE_TYPES, - 'wand.image.COLORSPACE_TYPES', - colorspace=colorspace_type) - r = library.MagickSetImageColorspace( - self.wand, - COLORSPACE_TYPES.index(colorspace_type) - ) - if not r: # pragma: no cover - self.raise_exception() - - @property - def compose(self): - """(:class:`basestring`) The type of image compose. - It's a string from :const:`COMPOSITE_OPERATORS` list. - It also can be set. - - .. versionadded:: 0.5.1 - """ - compose_index = library.MagickGetImageCompose(self.wand) - return COMPOSITE_OPERATORS[compose_index] - - @compose.setter - def compose(self, operator): - assertions.string_in_list(COMPOSITE_OPERATORS, - 'wand.image.COMPOSITE_OPERATORS', - compose=operator) - library.MagickSetImageCompose(self.wand, - COMPOSITE_OPERATORS.index(operator)) - - @property - def compression(self): - """(:class:`basestring`) The type of image compression. - It's a string from :const:`COMPRESSION_TYPES` list. - It also can be set. - - .. versionadded:: 0.3.6 - .. versionchanged:: 0.5.2 - Setting :attr:`compression` now sets both `image_info` - and `images` in the internal image stack. - """ - compression_index = library.MagickGetImageCompression(self.wand) - return COMPRESSION_TYPES[compression_index] - - @compression.setter - def compression(self, value): - assertions.string_in_list(COMPRESSION_TYPES, - 'wand.image.COMPRESSION_TYPES', - compression=value) - library.MagickSetCompression( - self.wand, - COMPRESSION_TYPES.index(value) - ) - library.MagickSetImageCompression( - self.wand, - COMPRESSION_TYPES.index(value) - ) - - @property - def compression_quality(self): - """(:class:`numbers.Integral`) Compression quality of this image. - - .. versionadded:: 0.2.0 - .. versionchanged:: 0.5.2 - Setting :attr:`compression_quality` now sets both `image_info` - and `images` in the internal image stack. - """ - return library.MagickGetImageCompressionQuality(self.wand) - - @compression_quality.setter - @manipulative - def compression_quality(self, quality): - """Set compression quality for the image. - - :param quality: new compression quality setting - :type quality: :class:`numbers.Integral` - - """ - assertions.assert_integer(compression_quality=quality) - library.MagickSetCompressionQuality(self.wand, quality) - r = library.MagickSetImageCompressionQuality(self.wand, quality) - if not r: # pragma: no cover - raise ValueError('Unable to set compression quality to ' + - repr(quality)) - - @property - def delay(self): - """(:class:`numbers.Integral`) The number of ticks between frames. - - .. versionadded:: 0.5.9 - """ - return library.MagickGetImageDelay(self.wand) - - @delay.setter - def delay(self, value): - assertions.assert_integer(delay=value) - library.MagickSetImageDelay(self.wand, value) - - @property - def depth(self): - """(:class:`numbers.Integral`) The depth of this image. - - .. versionadded:: 0.2.1 - - """ - return library.MagickGetImageDepth(self.wand) - - @depth.setter - @manipulative - def depth(self, depth): - r = library.MagickSetImageDepth(self.wand, depth) - if not r: # pragma: no cover - raise self.raise_exception() - - @property - def dispose(self): - """(:class:`basestring`) Controls how the image data is - handled during animations. Values are from :const:`DISPOSE_TYPES` - list, and can also be set. - - .. seealso:: - - `Dispose Images`__ section in ``Animation Basics`` article. - - __ https://www.imagemagick.org/Usage/anim_basics/#dispose_images - - .. versionadded:: 0.5.0 - """ - dispose_idx = library.MagickGetImageDispose(self.wand) - try: - return DISPOSE_TYPES[dispose_idx] - except IndexError: # pragma: no cover - return DISPOSE_TYPES[0] - - @dispose.setter - def dispose(self, value): - assertions.string_in_list(DISPOSE_TYPES, - 'wand.image.DISPOSE_TYPES', - dispose=value) - library.MagickSetImageDispose(self.wand, DISPOSE_TYPES.index(value)) - - @property - def font(self): - """(:class:`wand.font.Font`) The current font options.""" - if not self.font_path: - return None - return Font( - path=text(self.font_path), - size=self.font_size, - color=self.font_color, - antialias=self.antialias, - stroke_color=self.stroke_color, - stroke_width=self.stroke_width - ) - - @font.setter - @manipulative - def font(self, font): - if not isinstance(font, Font): - raise TypeError('font must be a wand.font.Font, not ' + repr(font)) - self.font_path = font.path - self.font_size = font.size - self.font_color = font.color - self.antialias = font.antialias - if font.stroke_color: - self.stroke_color = font.stroke_color - if font.stroke_width is not None: - self.stroke_width = font.stroke_width - - @property - def font_antialias(self): - """ - .. deprecated:: 0.5.0 - Use :attr:`antialias` instead. - """ - return self.antialias - - @font_antialias.setter - def font_antialias(self, antialias): - self.antialias = antialias - - @property - def font_color(self): - return Color(self.options['fill']) - - @font_color.setter - @manipulative - def font_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(font_color=color) - self.options['fill'] = color.string - - @property - def font_path(self): - """(:class:`basestring`) The path of the current font. - It also can be set. - - """ - font_str = None - font_p = library.MagickGetFont(self.wand) - if font_p: - font_str = text(ctypes.string_at(font_p)) - font_p = library.MagickRelinquishMemory(font_p) - return font_str - - @font_path.setter - @manipulative - def font_path(self, font): - font = binary(font) - r = library.MagickSetFont(self.wand, font) - if not r: # pragma: no cover - self.raise_exception() - - @property - def font_size(self): - """(:class:`numbers.Real`) The font size. It also can be set.""" - return library.MagickGetPointsize(self.wand) - - @font_size.setter - @manipulative - def font_size(self, size): - assertions.assert_real(font_size=size) - if size < 0.0: - raise ValueError('cannot be less than 0.0, but got ' + repr(size)) - r = library.MagickSetPointsize(self.wand, size) - if not r: # pragma: no cover - self.raise_exception() - - @property - def format(self): - """(:class:`basestring`) The image format. - - If you want to convert the image format, just reset this property:: - - assert isinstance(img, wand.image.Image) - img.format = 'png' - - It may raise :exc:`ValueError` when the format is unsupported. - - .. seealso:: - - `ImageMagick Image Formats`__ - ImageMagick uses an ASCII string known as *magick* (e.g. ``GIF``) - to identify file formats, algorithms acting as formats, - built-in patterns, and embedded profile types. - - __ http://www.imagemagick.org/script/formats.php - - .. versionadded:: 0.1.6 - - """ - fmt_str = None - fmt_p = library.MagickGetImageFormat(self.wand) - if fmt_p: - fmt_str = text(ctypes.string_at(fmt_p)) - fmt_p = library.MagickRelinquishMemory(fmt_p) - return fmt_str - - @format.setter - def format(self, fmt): - assertions.assert_string(format=fmt) - fmt = fmt.strip() - r = library.MagickSetImageFormat(self.wand, binary(fmt.upper())) - if not r: - raise ValueError(repr(fmt) + ' is unsupported format') - r = library.MagickSetFilename(self.wand, - b'buffer.' + binary(fmt.lower())) - if not r: # pragma: no cover - self.raise_exception() - - @property - def fuzz(self): - """(:class:`numbers.Real`) The normalized real number between ``0.0`` - and :attr:`quantum_range`. This property influences the accuracy of - :meth:`compare()`. - - .. versionadded:: 0.5.3 - """ - return library.MagickGetImageFuzz(self.wand) - - @fuzz.setter - def fuzz(self, value): - assertions.assert_real(fuzz=value) - library.MagickSetImageFuzz(self.wand, value) - - @property - def gravity(self): - """(:class:`basestring`) The text placement gravity used when - annotating with text. It's a string from :const:`GRAVITY_TYPES` - list. It also can be set. - - """ - gravity_index = library.MagickGetGravity(self.wand) - if not gravity_index: # pragma: no cover - self.raise_exception() - return GRAVITY_TYPES[gravity_index] - - @gravity.setter - @manipulative - def gravity(self, value): - assertions.string_in_list(GRAVITY_TYPES, - 'wand.image.GRAVITY_TYPES', - gravity=value) - library.MagickSetGravity(self.wand, GRAVITY_TYPES.index(value)) - - @property - def green_primary(self): - """(:class:`tuple`) The chromatic green primary point for the image. - With ImageMagick-6 the primary value is ``(x, y)`` coordinates; - however, ImageMagick-7 has ``(x, y, z)``. - - .. versionadded:: 0.5.2 - """ - x = ctypes.c_double(0.0) - y = ctypes.c_double(0.0) - r = None - p = None - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickGetImageGreenPrimary(self.wand, x, y) - p = (x.value, y.value) - else: # pragma: no cover - z = ctypes.c_double(0.0) - r = library.MagickGetImageGreenPrimary(self.wand, x, y, z) - p = (x.value, y.value, z.value) - if not r: # pragma: no cover - self.raise_exception() - return p - - @green_primary.setter - def green_primary(self, coordinates): - r = None - if not isinstance(coordinates, abc.Sequence): - raise TypeError('Primary must be a tuple') - if MAGICK_VERSION_NUMBER < 0x700: - x, y = coordinates - r = library.MagickSetImageGreenPrimary(self.wand, x, y) - else: # pragma: no cover - x, y, z = coordinates - r = library.MagickSetImageGreenPrimary(self.wand, x, y, z) - if not r: # pragma: no cover - self.raise_exception() - - @property - def height(self): - """(:class:`numbers.Integral`) The height of this image.""" - return library.MagickGetImageHeight(self.wand) - - @height.setter - @manipulative - def height(self, height): - assertions.assert_unsigned_integer(height=height) - library.MagickSetSize(self.wand, self.width, height) - - @property - def histogram(self): - """(:class:`HistogramDict`) The mapping that represents the histogram. - Keys are :class:`~wand.color.Color` objects, and values are - the number of pixels. - - .. tip:: - - True-color photos can have millions of color values. If performance - is more valuable than accuracy, remember to :meth:`quantize` the - image before generating a :class:`HistogramDict`. - - with Image(filename='hd_photo.jpg') as img: - img.quantize(255, 'RGB', 0, False, False) - hist = img.histogram - - .. versionadded:: 0.3.0 - - """ - return HistogramDict(self) - - @property - def interlace_scheme(self): - """(:class:`basestring`) The interlace used by the image. - See :const:`INTERLACE_TYPES`. - - .. versionadded:: 0.5.2 - - .. versionchanged:: 0.6.2 - The :attr:`interlace_scheme` property now points to the image. - Previously was pointing to the :c:struct:`MagickWand`. - """ - scheme_idx = library.MagickGetImageInterlaceScheme(self.wand) - return INTERLACE_TYPES[scheme_idx] - - @interlace_scheme.setter - def interlace_scheme(self, scheme): - assertions.string_in_list(INTERLACE_TYPES, - 'wand.image.INTERLACE_TYPES', - interlace_scheme=scheme) - scheme_idx = INTERLACE_TYPES.index(scheme) - library.MagickSetImageInterlaceScheme(self.wand, scheme_idx) - - @property - def interpolate_method(self): - """(:class:`basestring`) The interpolation method of the image. - See :const:`PIXEL_INTERPOLATE_METHODS`. - - .. versionadded:: 0.5.2 - """ - method_idx = library.MagickGetImageInterpolateMethod(self.wand) - return PIXEL_INTERPOLATE_METHODS[method_idx] - - @interpolate_method.setter - def interpolate_method(self, method): - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - interpolate_method=method) - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - library.MagickSetImageInterpolateMethod(self.wand, method_idx) - - @property - def kurtosis(self): - """(:class:`numbers.Real`) The kurtosis of the image. - - .. tip:: - - If you want both :attr:`kurtosis` & :attr:`skewness`, it - would be faster to call :meth:`kurtosis_channel()` directly. - - .. versionadded:: 0.5.3 - """ - k, _ = self.kurtosis_channel() - return k - - @property - def length_of_bytes(self): - """(:class:`numbers.Integral`) The original size, in bytes, - of the image read. This will return `0` if the image was modified in - a way that would invalidate the original length value. - - .. versionadded:: 0.5.4 - """ - size_ptr = ctypes.c_size_t(0) - library.MagickGetImageLength(self.wand, ctypes.byref(size_ptr)) - return size_ptr.value - - @property - def loop(self): - """(:class:`numbers.Integral`) Number of frame iterations. - A value of ``0`` will loop forever.""" - return library.MagickGetImageIterations(self.wand) - - @loop.setter - def loop(self, iterations): - assertions.assert_unsigned_integer(loop=iterations) - library.MagickSetImageIterations(self.wand, iterations) - - @property - def matte_color(self): - """(:class:`wand.color.Color`) The color value of the matte channel. - This can also be set. - - .. versionadded:: 0.4.1 - """ - pixel = library.NewPixelWand() - result = library.MagickGetImageMatteColor(self.wand, pixel) - if result: - color = Color.from_pixelwand(pixel) - pixel = library.DestroyPixelWand(pixel) - return color - else: # pragma: no cover - self.raise_exception() - - @matte_color.setter - @manipulative - def matte_color(self, color): - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(matte_color=color) - with color: - result = library.MagickSetImageMatteColor(self.wand, - color.resource) - if not result: # pragma: no cover - self.raise_exception() - - @property - def maxima(self): - """(:class:`numbers.Real`) The maximum quantum value within the image. - Value between 0.0 and :attr:`quantum_range` - - .. tip:: - - If you want both :attr:`maxima` & :attr:`minima`, - it would be faster to call :meth:`range_channel()` directly. - - .. versionadded:: 0.5.3 - """ - _, max_q = self.range_channel() - return max_q - - @property - def mean(self): - """(:class:`numbers.Real`) The mean of the image, and have a value - between 0.0 and :attr:`quantum_range` - - .. tip:: - - If you want both :attr:`mean` & :attr:`standard_deviation`, it - would be faster to call :meth:`mean_channel()` directly. - - .. versionadded:: 0.5.3 - """ - m, _ = self.mean_channel() - return m - - @property - def minima(self): - """(:class:`numbers.Real`) The minimum quantum value within the image. - Value between 0.0 and :attr:`quantum_range` - - .. tip:: - - If you want both :attr:`maxima` & :attr:`minima`, - it would be faster to call :meth:`range_channel()` directly. - - .. versionadded:: 0.5.3 - """ - min_q, _ = self.range_channel() - return min_q - - @property - def orientation(self): - """(:class:`basestring`) The image orientation. It's a string from - :const:`ORIENTATION_TYPES` list. It also can be set. - - .. versionadded:: 0.3.0 - - """ - orientation_index = library.MagickGetImageOrientation(self.wand) - try: - return ORIENTATION_TYPES[orientation_index] - except IndexError: # pragma: no cover - return ORIENTATION_TYPES[0] - - @orientation.setter - @manipulative - def orientation(self, value): - assertions.string_in_list(ORIENTATION_TYPES, - 'wand.image.ORIENTATION_TYPES', - orientation=value) - index = ORIENTATION_TYPES.index(value) - library.MagickSetImageOrientation(self.wand, index) - - @property - def page(self): - """The dimensions and offset of this Wand's page as a 4-tuple: - ``(width, height, x, y)``. - - .. code:: - - with Image(filename='wizard:') as img: - img.page = (595, 842, 0, 0) - - Note that since it is based on the virtual canvas, it may not equal the - dimensions of an image. See the ImageMagick documentation on the - virtual canvas for more information. - - This attribute can also be set by using a named papersize. For - example:: - - with Image(filename='wizard:') as img: - img.page = 'a4' - - .. versionadded:: 0.4.3 - - .. versionchanged:: 0.6.4 - Added support for setting by papersize. - """ - w = ctypes.c_size_t() - h = ctypes.c_size_t() - x = ctypes.c_ssize_t() - y = ctypes.c_ssize_t() - r = library.MagickGetImagePage(self.wand, w, h, x, y) - if not r: # pragma: no cover - self.raise_exception() - return int(w.value), int(h.value), int(x.value), int(y.value) - - @page.setter - @manipulative - def page(self, newpage): - if isinstance(newpage, string_type): - c_ptr = libmagick.GetPageGeometry(newpage.encode()) - ri = RectangleInfo() - c_ptr = ctypes.cast(c_ptr, ctypes.c_char_p) - libmagick.ParseAbsoluteGeometry(c_ptr, ctypes.byref(ri)) - newpage = (ri.width, ri.height, ri.x, ri.y) - libmagick.DestroyString(c_ptr) - del ri - if isinstance(newpage, abc.Sequence): - w, h, x, y = newpage - else: - raise TypeError("page layout must be 4-tuple") - r = library.MagickSetImagePage(self.wand, w, h, x, y) - if not r: # pragma: no cover - self.raise_exception() - - @property - def page_height(self): - """(:class:`numbers.Integral`) The height of the page for this wand. - - .. versionadded:: 0.4.3 - - """ - return self.page[1] - - @page_height.setter - @manipulative - def page_height(self, height): - newpage = list(self.page) - newpage[1] = height - self.page = newpage - - @property - def page_width(self): - """(:class:`numbers.Integral`) The width of the page for this wand. - - .. versionadded:: 0.4.3 - - """ - return self.page[0] - - @page_width.setter - @manipulative - def page_width(self, width): - newpage = list(self.page) - newpage[0] = width - self.page = newpage - - @property - def page_x(self): - """(:class:`numbers.Integral`) The X-offset of the page for this wand. - - .. versionadded:: 0.4.3 - - """ - return self.page[2] - - @page_x.setter - @manipulative - def page_x(self, x): - newpage = list(self.page) - newpage[2] = x - self.page = newpage - - @property - def page_y(self): - """(:class:`numbers.Integral`) The Y-offset of the page for this wand. - - .. versionadded:: 0.4.3 - - """ - return self.page[3] - - @page_y.setter - @manipulative - def page_y(self, y): - newpage = list(self.page) - newpage[3] = y - self.page = newpage - - @property - def quantum_range(self): - """(`:class:`numbers.Integral`) The maximum value of a color - channel that is supported by the imagemagick library. - - .. versionadded:: 0.2.0 - - """ - result = ctypes.c_size_t() - library.MagickGetQuantumRange(ctypes.byref(result)) - return result.value - - @property - def red_primary(self): - """(:class:`tuple`) The chromatic red primary point for the image. - With ImageMagick-6 the primary value is ``(x, y)`` coordinates; - however, ImageMagick-7 has ``(x, y, z)``. - - .. versionadded:: 0.5.2 - """ - x = ctypes.c_double(0.0) - y = ctypes.c_double(0.0) - r = None - p = None - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickGetImageRedPrimary(self.wand, x, y) - p = (x.value, y.value) - else: # pragma: no cover - z = ctypes.c_double(0.0) - r = library.MagickGetImageRedPrimary(self.wand, x, y, z) - p = (x.value, y.value, z.value) - if not r: # pragma: no cover - self.raise_exception() - return p - - @red_primary.setter - def red_primary(self, coordinates): - r = None - if not isinstance(coordinates, abc.Sequence): - raise TypeError('Primary must be a tuple') - if MAGICK_VERSION_NUMBER < 0x700: - x, y = coordinates - r = library.MagickSetImageRedPrimary(self.wand, x, y) - else: # pragma: no cover - x, y, z = coordinates - r = library.MagickSetImageRedPrimary(self.wand, x, y, z) - if not r: # pragma: no cover - self.raise_exception() - - @property - def rendering_intent(self): - """(:class:`basestring`) PNG rendering intent. See - :const:`RENDERING_INTENT_TYPES` for valid options. - - .. versionadded:: 0.5.4 - """ - ri_index = library.MagickGetImageRenderingIntent(self.wand) - return RENDERING_INTENT_TYPES[ri_index] - - @rendering_intent.setter - def rendering_intent(self, value): - assertions.string_in_list(RENDERING_INTENT_TYPES, - 'wand.image.RENDERING_INTENT_TYPES', - rendering_intent=value) - ri_index = RENDERING_INTENT_TYPES.index(value) - library.MagickSetImageRenderingIntent(self.wand, ri_index) - - @property - def resolution(self): - """(:class:`tuple`) Resolution of this image. - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.5.8 - Resolution returns a tuple of float values to - match ImageMagick's behavior. - """ - x = ctypes.c_double(0.0) - y = ctypes.c_double(0.0) - r = library.MagickGetImageResolution(self.wand, x, y) - if not r: # pragma: no cover - self.raise_exception() - return x.value, y.value - - @resolution.setter - @manipulative - def resolution(self, geometry): - if isinstance(geometry, abc.Sequence): - x, y = geometry - elif isinstance(geometry, numbers.Real): - x, y = geometry, geometry - else: - raise TypeError('resolution must be a (x, y) pair or a float ' - 'of the same x/y') - if self.size == (0, 0): - r = library.MagickSetResolution(self.wand, x, y) - else: - r = library.MagickSetImageResolution(self.wand, x, y) - if not r: # pragma: no cover - self.raise_exception() - - @property - def sampling_factors(self): - """(:class:`tuple`) Factors used in sampling data streams. - This can be set by given it a string ``"4:2:2"``, or tuple of numbers - ``(2, 1, 1)``. However the given string value will be parsed to aspect - ratio (i.e. ``"4:2:2"`` internally becomes ``"2,1"``). - - .. note:: - This property is only used by YUV, DPX, & EXR encoders. For - JPEG & TIFF set ``"jpeg:sampling-factor"`` on - :attr:`Image.options` dictionary:: - - with Image(filename='input.jpg') as img: - img.options['jpeg:sampling-factor'] = '2x1' - - .. versionadded:: 0.6.3 - """ - factors_len = ctypes.c_size_t(0) - factors = library.MagickGetSamplingFactors(self.wand, - ctypes.byref(factors_len)) - factors_tuple = tuple(factors[x] for x in xrange(factors_len.value)) - factors = library.MagickRelinquishMemory(factors) - return factors_tuple - - @sampling_factors.setter - def sampling_factors(self, factors): - if isinstance(factors, string_type): - geometry_info = GeometryInfo() - flags = libmagick.ParseGeometry(binary(factors), - ctypes.byref(geometry_info)) - if (flags & geometry_info.SigmaValue) == 0: - factors = (geometry_info.rho, geometry_info.rho) - else: - factors = (geometry_info.rho, geometry_info.sigma) - elif not isinstance(factors, abc.Sequence): - raise TypeError('sampling_factors must be a sequence of real ' - 'numbers, not ' + repr(factors)) - factors_len = len(factors) - factors_ptr = (ctypes.c_double * factors_len)(*factors) - library.MagickSetSamplingFactors(self.wand, factors_len, factors_ptr) - - @property - def scene(self): - """(:class:`numbers.Integral`) The scene number of the current frame - within an animated image. - - .. versionadded:: 0.5.4 - """ - return library.MagickGetImageScene(self.wand) - - @scene.setter - def scene(self, value): - assertions.assert_unsigned_integer(scene=value) - library.MagickSetImageScene(self.wand, value) - - @property - def seed(self): - """(:class:`numbers.Integral`) The seed for random number generator. - - .. warning:: - - This property is only available with ImageMagick 7.0.8-41, or - greater. - - .. versionadded:: 0.5.5 - """ - return self._seed - - @seed.setter - def seed(self, value): - if library.MagickSetSeed is None: - msg = 'Property requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - assertions.assert_unsigned_integer(seed=value) - self._seed = value - library.MagickSetSeed(self.wand, value) - - @property - def signature(self): - """(:class:`str`) The SHA-256 message digest for the image pixel - stream. - - .. versionadded:: 0.1.9 - - """ - sig_str = None - sig_p = library.MagickGetImageSignature(self.wand) - if sig_p: - sig_str = text(ctypes.string_at(sig_p)) - sig_p = library.MagickRelinquishMemory(sig_p) - return sig_str - - @property - def size(self): - """(:class:`tuple`) The pair of (:attr:`width`, :attr:`height`). - - .. note:: - - When working with animations, or other layer-based image formats, - the :attr:`width` & :attr:`height` properties are referencing the - last frame read into the image stack. To get the :attr:`size` - of the entire animated images, call - :meth:`Image.coalesce() ` method - immediately after reading the image. - """ - return self.width, self.height - - @property - def skewness(self): - """(:class:`numbers.Real`) The skewness of the image. - - .. tip:: - - If you want both :attr:`kurtosis` & :attr:`skewness`, it - would be faster to call :meth:`kurtosis_channel()` directly. - - .. versionadded:: 0.5.3 - """ - _, s = self.kurtosis_channel() - return s - - @property - def standard_deviation(self): - """(:class:`numbers.Real`) The standard deviation of the image. - - .. tip:: - - If you want both :attr:`mean` & :attr:`standard_deviation`, it - would be faster to call :meth:`mean_channel()` directly. - - .. versionadded:: 0.5.3 - """ - _, s = self.mean_channel() - return s - - @property - def stroke_color(self): - stroke = self.options['stroke'] - return Color(stroke) if stroke else None - - @stroke_color.setter - def stroke_color(self, color): - if isinstance(color, string_type): - color = Color(color) - if isinstance(color, Color): - self.options['stroke'] = color.string - elif color is None: - del self.options['stroke'] - else: - raise TypeError('stroke_color must be a wand.color.Color, not ' + - repr(color)) - - @property - def stroke_width(self): - strokewidth = self.options['strokewidth'] - return float(strokewidth) if strokewidth else None - - @stroke_width.setter - def stroke_width(self, width): - assertions.assert_real(stroke_width=width) - self.options['strokewidth'] = str(width) - - @property - def ticks_per_second(self): - """(:class:`numbers.Integral`) Internal clock for animated images. - .. versionadded:: 0.5.4 - """ - return library.MagickGetImageTicksPerSecond(self.wand) - - @ticks_per_second.setter - def ticks_per_second(self, value): - assertions.assert_unsigned_integer(ticks_per_second=value) - r = library.MagickSetImageTicksPerSecond(self.wand, value) - if not r: # pragma: no cover - self.raise_exception() - - @property - def type(self): - """(:class:`basestring`) The image type. - - Defines image type as in :const:`IMAGE_TYPES` enumeration. - - It may raise :exc:`ValueError` when the type is unknown. - - .. versionadded:: 0.2.2 - - """ - image_type_index = library.MagickGetImageType(self.wand) - if not image_type_index: # pragma: no cover - self.raise_exception() - return IMAGE_TYPES[text(image_type_index)] - - @type.setter - @manipulative - def type(self, image_type): - assertions.string_in_list(IMAGE_TYPES, 'wand.image.IMAGE_TYPES', - type=image_type) - r = library.MagickSetImageType(self.wand, - IMAGE_TYPES.index(image_type)) - if not r: # pragma: no cover - self.raise_exception() - - @property - def units(self): - """(:class:`basestring`) The resolution units of this image.""" - r = library.MagickGetImageUnits(self.wand) - return UNIT_TYPES[text(r)] - - @units.setter - @manipulative - def units(self, units): - assertions.string_in_list(UNIT_TYPES, 'wand.image.UNIT_TYPES', - units=units) - r = library.MagickSetImageUnits(self.wand, UNIT_TYPES.index(units)) - if not r: # pragma: no cover - self.raise_exception() - - @property - def virtual_pixel(self): - """(:class:`basestring`) The virtual pixel of image. - This can also be set with a value from :const:`VIRTUAL_PIXEL_METHOD` - ... versionadded:: 0.4.1 - """ - method_index = library.MagickGetImageVirtualPixelMethod(self.wand) - return VIRTUAL_PIXEL_METHOD[method_index] - - @virtual_pixel.setter - def virtual_pixel(self, method): - assertions.string_in_list(VIRTUAL_PIXEL_METHOD, - 'wand.image.VIRTUAL_PIXEL_METHOD', - virtual_pixel=method) - library.MagickSetImageVirtualPixelMethod( - self.wand, - VIRTUAL_PIXEL_METHOD.index(method) - ) - - @property - def wand(self): - """Internal pointer to the MagickWand instance. It may raise - :exc:`ClosedImageError` when the instance has destroyed already. - - """ - try: - return self.resource - except DestroyedResourceError: - raise ClosedImageError(repr(self) + ' is closed already') - - @wand.setter - def wand(self, wand): - try: - self.resource = wand - except TypeError: - raise TypeError(repr(wand) + ' is not a MagickWand instance') - - @wand.deleter - def wand(self): - del self.resource - - @property - def width(self): - """(:class:`numbers.Integral`) The width of this image.""" - return library.MagickGetImageWidth(self.wand) - - @width.setter - @manipulative - def width(self, width): - assertions.assert_unsigned_integer(width=width) - library.MagickSetSize(self.wand, width, self.height) - - @property - def white_point(self): - """(:class:`tuple`) The chromatic white point for the image. - With ImageMagick-6 the primary value is ``(x, y)`` coordinates; - however, ImageMagick-7 has ``(x, y, z)``. - - .. versionadded:: 0.5.2 - """ - x = ctypes.c_double(0.0) - y = ctypes.c_double(0.0) - r = None - p = None - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickGetImageWhitePoint(self.wand, x, y) - p = (x.value, y.value) - else: # pragma: no cover - z = ctypes.c_double(0.0) - r = library.MagickGetImageWhitePoint(self.wand, x, y, z) - p = (x.value, y.value, z.value) - if not r: # pragma: no cover - self.raise_exception() - return p - - @white_point.setter - def white_point(self, coordinates): - r = None - if not isinstance(coordinates, abc.Sequence): - raise TypeError('Primary must be a tuple') - if MAGICK_VERSION_NUMBER < 0x700: - x, y = coordinates - r = library.MagickSetImageWhitePoint(self.wand, x, y) - else: # pragma: no cover - x, y, z = coordinates - r = library.MagickSetImageWhitePoint(self.wand, x, y, z) - if not r: # pragma: no cover - self.raise_exception() - - @manipulative - def _auto_orient(self): - """Fallback for :attr:`auto_orient()` method - (which wraps :c:func:`MagickAutoOrientImage`), - fixes orientation by checking EXIF data. - - .. versionadded:: 0.4.1 - - """ - v_ptr = library.MagickGetImageProperty(self.wand, - b'exif:orientation') - if v_ptr: - exif_orientation = ctypes.string_at(v_ptr) - v_ptr = library.MagickRelinquishMemory(v_ptr) - else: - return - - if not exif_orientation: - return - - orientation_type = ORIENTATION_TYPES[int(exif_orientation)] - - fn_lookup = { - 'undefined': None, - 'top_left': None, - 'top_right': self.flop, - 'bottom_right': functools.partial(self.rotate, degree=180.0), - 'bottom_left': self.flip, - 'left_top': self.transpose, - 'right_top': functools.partial(self.rotate, degree=90.0), - 'right_bottom': self.transverse, - 'left_bottom': functools.partial(self.rotate, degree=270.0) - } - - fn = fn_lookup.get(orientation_type) - - if not fn: - return - - fn() - self.orientation = 'top_left' - - def _channel_to_mask(self, value): - """Attempts to resolve user input into a :c:type:`ChannelType` - bit-mask. User input can be an integer, a string defined in - :const:`CHANNELS`, or a string following ImageMagick's `CLI format`__. - - __ https://imagemagick.org/script/command-line-options.php#channel - - .. code:: - - # User generated bit-mask. - mask = self._channel_to_mask(CHANNELS['red'] | CHANNELS['green']) - # Defined constant. - mask = self._channel_to_mask('red') - # CLI format. - mask = self._channel_to_mask('RGB,Sync') - - :param value: Mixed user input. - :type value: :class:`numbers.Integral` or :class:`basestring` - :returns: Bit-mask constant. - :rtype: :class:`int` - - .. versionadded:: 0.5.5 - """ - mask = -1 - if isinstance(value, numbers.Integral) and not isinstance(value, bool): - mask = value - elif isinstance(value, string_type): - if value in CHANNELS: - mask = CHANNELS[value] - elif libmagick.ParseChannelOption: - mask = libmagick.ParseChannelOption(binary(value)) - else: - raise TypeError(repr(value) + ' is an invalid channel type' - '; see wand.image.CHANNELS dictionary') - if mask < 0: - raise ValueError('expected value from wand.image.CHANNELS, not ' - + repr(value)) - return mask - - def _gravity_to_offset(self, gravity, width, height): - """Calculate the top/left offset by a given gravity. - - Some methods in MagickWand's C-API do not respect gravity, but - instead, expect a x/y offset. This is confusing to folks coming from - the CLI documentation that does respect gravity - - :param gravity: Value from :const:`GRAVITY_TYPES`. - :type gravity: :class:`basestring` - :raises: :class:`ValueError` if gravity is no known. - :returns: :class:`numbers.Intergal` top, :class:`numbers.Intergal` left - - .. versionadded:: 0.5.3 - """ - top, left = 0, 0 - assertions.string_in_list(GRAVITY_TYPES, 'wand.image.GRAVITY_TYPES', - gravity=gravity) - # Set `top` based on given gravity - if gravity in ('north_west', 'north', 'north_east'): - top = 0 - elif gravity in ('west', 'center', 'east'): - top = int(self.height / 2) - int(height / 2) - elif gravity in ('south_west', 'south', 'south_east'): - top = self.height - height - # Set `left` based on given gravity - if gravity in ('north_west', 'west', 'south_west'): - left = 0 - elif gravity in ('north', 'center', 'south'): - left = int(self.width / 2) - int(width / 2) - elif gravity in ('north_east', 'east', 'south_east'): - left = self.width - width - return top, left - - @manipulative - @trap_exception - def adaptive_blur(self, radius=0.0, sigma=0.0, channel=None): - """Adaptively blurs the image by decreasing Gaussian as the operator - approaches detected edges. - - :see: Example of :ref:`adaptive_blur`. - - :param radius: size of gaussian aperture. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of the gaussian filter. - :type sigma: :class:`numbers.Real` - :param channel: Apply the blur effect on a specific channel. - See :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument - """ - assertions.assert_real(radius=radius, sigma=sigma) - if channel is None: - r = library.MagickAdaptiveBlurImage(self.wand, radius, sigma) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickAdaptiveBlurImageChannel(self.wand, - channel_ch, - radius, - sigma) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickAdaptiveBlurImage(self.wand, radius, sigma) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def adaptive_resize(self, columns=None, rows=None): - """Adaptively resize image by applying Mesh interpolation. - - :param columns: width of resized image. - :type columns: :class:`numbers.Integral` - :param rows: height of resized image. - :type rows: :class:`numbers.Integral` - - .. versionadded:: 0.5.3 - """ - if columns is None: - columns = self.width - if rows is None: - rows = self.height - assertions.assert_integer(columns=columns, rows=rows) - return library.MagickAdaptiveResizeImage(self.wand, columns, rows) - - @manipulative - @trap_exception - def adaptive_sharpen(self, radius=0.0, sigma=0.0, channel=None): - """Adaptively sharpens the image by sharpening more intensely near - image edges and less intensely far from edges. - - :see: Example of :ref:`adaptive_sharpen`. - - :param radius: size of gaussian aperture. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of the gaussian filter. - :type sigma: :class:`numbers.Real` - :param channel: Apply the sharpen effect on a specific channel. - See :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument - """ - assertions.assert_real(radius=radius, sigma=sigma) - if channel is None: - r = library.MagickAdaptiveSharpenImage(self.wand, radius, sigma) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickAdaptiveSharpenImageChannel(self.wand, - channel_ch, - radius, - sigma) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickAdaptiveSharpenImage(self.wand, - radius, - sigma) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - def adaptive_threshold(self, width, height, offset=0.0): - """Applies threshold for each pixel based on neighboring pixel values. - - :param width: size of neighboring pixels on the X-axis. - :type width: :class:`numbers.Integral` - :param height: size of neighboring pixels on the Y-axis. - :type height: :class:`numbers.Integral` - :param offset: normalized number between `0.0` and - :attr:`quantum_range`. Forces the pixels to black if - values are below ``offset``. - :type offset: :class:`numbers.Real` - - .. versionadded:: 0.5.3 - """ - assertions.assert_integer(width=width, height=height) - assertions.assert_real(offset=offset) - if MAGICK_VERSION_NUMBER < 0x700: - offset = int(offset) - return library.MagickAdaptiveThresholdImage(self.wand, width, - height, offset) - - @manipulative - @trap_exception - def annotate(self, text, drawing_wand, left=0, baseline=0, angle=0): - """Draws text on an image. This method differs from :meth:`caption()` - as it uses :class:`~wand.drawing.Drawing` class to manage the - font configuration & style context. - - .. code:: - - from wand.drawing import Drawing - from wand.image import Image - - with Image(filename='input.jpg') as img: - with Drawing() as ctx: - ctx.font_family = 'Times New Roman, Nimbus Roman No9' - ctx.font_size = 18 - ctx.text_decoration = 'underline' - ctx.text_kerning = -1 - img.annotate('Hello World', ctx, left=20, baseline=50) - img.save(filename='output.jpg') - - :param text: String to annotate on image. - :type text: :class:`basestring` - :param drawing_wand: Font configuration & style context. - :type text: :class:`wand.drawing.Drawing` - :param left: X-axis offset of the text baseline. - :type left: :class:`numbers.Real` - :param baseline: Y-axis offset of the bottom of the text. - :type baseline: :class:`numbers.Real` - :param angle: Degree rotation to draw text at. - :type angle: :class:`numbers.Real` - - .. versionadded:: 0.5.6 - """ - from .drawing import Drawing - if not isinstance(drawing_wand, Drawing): - raise TypeError('drawing_wand must be in instances of ' + - 'wand.drawing.Drawing, not ' + repr(drawing_wand)) - assertions.assert_real(left=left, baseline=baseline, angle=angle) - btext = binary(text) - return library.MagickAnnotateImage(self.wand, drawing_wand.resource, - left, baseline, angle, btext) - - @manipulative - @trap_exception - def auto_gamma(self): - """Adjust the gamma level of an image. - - .. versionadded:: 0.5.4 - """ - return library.MagickAutoGammaImage(self.wand) - - @manipulative - @trap_exception - def auto_level(self): - """Scale the minimum and maximum values to a full quantum range. - - .. versionadded:: 0.5.4 - """ - return library.MagickAutoLevelImage(self.wand) - - @manipulative - @trap_exception - def auto_orient(self): - """Adjusts an image so that its orientation is suitable - for viewing (i.e. top-left orientation). If available it uses - :c:func:`MagickAutoOrientImage` (was added in ImageMagick 6.8.9+) - if you have an older magick library, - it will use :attr:`_auto_orient()` method for fallback. - - .. versionadded:: 0.4.1 - - """ - try: - return library.MagickAutoOrientImage(self.wand) - except AttributeError: # pragma: no cover - self._auto_orient() - return True - - @manipulative - @trap_exception - def auto_threshold(self, method='kapur'): - """Automatically performs threshold method to reduce grayscale data - down to a binary black & white image. Included algorithms are - Kapur, Otsu, and Triangle methods. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param method: Which threshold method to apply. - See :const:`AUTO_THRESHOLD_METHODS`. - Defaults to ``'kapur'``. - :type method: :class:`basestring` - :raises WandLibraryVersionError: if function is not available on - system's library. - - .. versionadded:: 0.5.5 - """ - if library.MagickAutoThresholdImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - assertions.string_in_list(AUTO_THRESHOLD_METHODS, - 'wand.image.AUTO_THRESHOLD_METHODS', - method=method) - method_idx = AUTO_THRESHOLD_METHODS.index(method) - return library.MagickAutoThresholdImage(self.wand, method_idx) - - @manipulative - @trap_exception - def black_threshold(self, threshold): - """Forces all pixels above a given color as black. Leaves pixels - above threshold unaltered. - - :param threshold: Color to be referenced as a threshold. - :type threshold: :class:`Color` - - .. versionadded:: 0.5.3 - """ - if isinstance(threshold, string_type): - threshold = Color(threshold) - assertions.assert_color(threshold=threshold) - with threshold: - r = library.MagickBlackThresholdImage(self.wand, - threshold.resource) - return r - - @manipulative - @trap_exception - def blue_shift(self, factor=1.5): - """Mutes colors of the image by shifting blue values. - - :see: Example of :ref:`blue_shift` - - :param factor: Amount to adjust values. - :type factor: :class:`numbers.Real` - - .. versionadded:: 0.5.3 - """ - assertions.assert_real(factor=factor) - return library.MagickBlueShiftImage(self.wand, factor) - - @manipulative - @trap_exception - def blur(self, radius=0.0, sigma=0.0, channel=None): - """Blurs the image. Convolve the image with a gaussian operator - of the given ``radius`` and standard deviation (``sigma``). - For reasonable results, the ``radius`` should be larger - than ``sigma``. Use a ``radius`` of 0 and :meth:`blur()` selects - a suitable ``radius`` for you. - - :see: Example of :ref:`blur`. - - :param radius: the radius of the, in pixels, - not counting the center pixel. Default is ``0.0``. - :type radius: :class:`numbers.Real` - :param sigma: the standard deviation of the, in pixels. Default value - is ``0.0``. - :type sigma: :class:`numbers.Real` - :param channel: Optional color channel to apply blur. See - :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.4.5 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument. - - .. versionchanged:: 0.5.7 - Positional arguments ``radius`` & ``sigman`` have been converted to - key-word arguments. - """ - assertions.assert_real(radius=radius, sigma=sigma) - if channel is None: - r = library.MagickBlurImage(self.wand, radius, sigma) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickBlurImageChannel(self.wand, - channel_ch, - radius, - sigma) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickBlurImage(self.wand, radius, sigma) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @trap_exception - def border(self, color, width, height, compose="copy"): - """Surrounds the image with a border. - - :param bordercolor: the border color pixel wand - :type image: :class:`~wand.color.Color` - :param width: the border width - :type width: :class:`numbers.Integral` - :param height: the border height - :type height: :class:`numbers.Integral` - :param compose: Use composite operator when applying frame. Only used - if called with ImageMagick 7+. - :type compose: :class:`basestring` - - .. versionadded:: 0.3.0 - .. versionchanged:: 0.5.0 - Added ``compose`` paramater, and ImageMagick 7 support. - """ - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - with color: - if MAGICK_VERSION_NUMBER < 0x700: - result = library.MagickBorderImage(self.wand, color.resource, - width, height) - else: # pragma: no cover - assertions.string_in_list(COMPOSITE_OPERATORS, - 'wand.image.COMPOSITE_OPERATORS', - compose=compose) - compose_idx = COMPOSITE_OPERATORS.index(compose) - result = library.MagickBorderImage(self.wand, color.resource, - width, height, compose_idx) - return result - - @manipulative - @trap_exception - def brightness_contrast(self, brightness=0.0, contrast=0.0, channel=None): - """Converts ``brightness`` & ``contrast`` parameters into a slope & - intercept, and applies a polynomial function. - - :param brightness: between ``-100.0`` and ``100.0``. Default is ``0.0`` - for unchanged. - :type brightness: :class:`numbers.Real` - :param contrast: between ``-100.0`` and ``100.0``. Default is ``0.0`` - for unchanged. - :type contrast: :class:`numbers.Real` - :param channel: Isolate a single color channel to apply contrast. - See :const:`CHANNELS`. - - .. versionadded:: 0.5.4 - - .. versionchanged:: 0.5.5 - Optional ``channel`` argument added. - """ - assertions.assert_real(brightness=brightness, contrast=contrast) - if channel is None: - r = library.MagickBrightnessContrastImage(self.wand, - brightness, - contrast) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickBrightnessContrastImageChannel(self.wand, - channel_ch, - brightness, - contrast) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickBrightnessContrastImage(self.wand, - brightness, - contrast) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def canny(self, radius=0.0, sigma=1.0, lower_percent=0.1, - upper_percent=0.3): - """Detect edges by leveraging a multi-stage Canny algorithm. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param radius: Size of gaussian filter. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of gaussian filter. - :type sigma: :class:`numbers.Real` - :param lower_percent: Normalized lower threshold. Values between - ``0.0`` (0%) and ``1.0`` (100%). The default - value is ``0.1`` or 10%. - :type lower_percent: :class:`numbers.Real` - :param upper_percent: Normalized upper threshold. Values between - ``0.0`` (0%) and ``1.0`` (100%). The default - value is ``0.3`` or 30%. - :type upper_percent: :class:`numbers.Real` - :raises WandLibraryVersionError: if function is not available on - system's library. - - .. versionadded:: 0.5.5 - """ - if library.MagickCannyEdgeImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - assertions.assert_real(radius=radius, sigma=sigma, - lower_percent=lower_percent, - upper_percent=upper_percent) - return library.MagickCannyEdgeImage(self.wand, radius, sigma, - lower_percent, upper_percent) - - @manipulative - def caption(self, text, left=0, top=0, width=None, height=None, font=None, - gravity=None): - """Writes a caption ``text`` into the position. - - :param text: text to write - :type text: :class:`basestring` - :param left: x offset in pixels - :type left: :class:`numbers.Integral` - :param top: y offset in pixels - :type top: :class:`numbers.Integral` - :param width: width of caption in pixels. - default is :attr:`width` of the image - :type width: :class:`numbers.Integral` - :param height: height of caption in pixels. - default is :attr:`height` of the image - :type height: :class:`numbers.Integral` - :param font: font to use. default is :attr:`font` of the image - :type font: :class:`wand.font.Font` - :param gravity: text placement gravity. - uses the current :attr:`gravity` setting of the image - by default - :type gravity: :class:`basestring` - - .. versionadded:: 0.3.0 - - """ - assertions.assert_integer(left=left, top=top) - if font is not None and not isinstance(font, Font): - raise TypeError('font must be a wand.font.Font, not ' + repr(font)) - if gravity is not None: - assertions.string_in_list(GRAVITY_TYPES, - 'wand.image.GRAVITY_TYPES', - gravity=gravity) - if width is None: - width = self.width - left - else: - assertions.assert_integer(width=width) - if height is None: - height = self.height - top - else: - assertions.assert_integer(height=height) - if font is None: - try: - font = self.font - if font is None: - raise TypeError() - except TypeError: - raise TypeError('font must be specified or existing in image') - with Image() as textboard: - library.MagickSetSize(textboard.wand, width, height) - textboard.font = font - textboard.gravity = gravity or self.gravity - with Color('transparent') as background_color: - library.MagickSetBackgroundColor(textboard.wand, - background_color.resource) - textboard.read(filename=b'caption:' + text.encode('utf-8')) - self.composite(textboard, left, top) - - def cdl(self, ccc): - """Alias for :meth:`color_decision_list`. - - .. versionadded:: 0.5.7 - """ - return self.color_decision_list(ccc) - - @trap_exception - def charcoal(self, radius, sigma): - """Transform an image into a simulated charcoal drawing. - - :see: Example of :ref:`charcoal`. - - :param radius: The size of the Gaussian operator. - :type radius: :class:`numbers.Real` - :param sigma: The standard deviation of the Gaussian. - :type sigma: :class:`numbers.Real` - - .. versionadded:: 0.5.3 - """ - assertions.assert_real(radius=radius, sigma=sigma) - return library.MagickCharcoalImage(self.wand, radius, sigma) - - @manipulative - @trap_exception - def chop(self, width=None, height=None, x=None, y=None, gravity=None): - """Removes a region of an image, and reduces the image size - accordingly. - - :param width: Size of region. - :type width: :class:`numbers.Integral` - :param height: Size of region. - :type height: :class:`numbers.Integral` - :param x: Offset on the X-axis. - :type x: :class:`numbers.Integral` - :param y: Offset on the Y-axis. - :type y: :class:`numbers.Integral` - :param gravity: Helper to auto-calculate offset. - See :const:`GRAVITY_TYPES`. - :type gravity: :class:`basestring` - - .. versionadded:: 0.5.5 - - .. versionchanged:: 0.6.8 - Added ``gravity`` argument. - """ - if width is None or width == 0: - width = self.width - if height is None or height == 0: - height = self.height - assertions.assert_unsigned_integer(width=width, height=height) - if gravity is None: - if x is None: - x = 0 - if y is None: - y = 0 - else: - if x is not None or y is not None: - raise ValueError('x & y can not be used with gravity.') - y, x = self._gravity_to_offset(gravity, width, height) - assertions.assert_integer(x=x, y=y) - return library.MagickChopImage(self.wand, width, height, x, y) - - @manipulative - @trap_exception - def clahe(self, width, height, number_bins, clip_limit): - """Contrast limited adaptive histogram equalization. - - .. warning:: - - The CLAHE method is only available with ImageMagick-7. - - :param width: Tile division width. - :type width: :class:`numbers.Integral` - :param height: Tile division height. - :type height: :class:`numbers.Integral` - :param number_bins: Histogram bins. - :type number_bins: :class:`numbers.Real` - :param clip_limit: contrast limit. - :type clip_limit: :class:`numbers.Real` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickCLAHEImage is None: - msg = 'CLAHE method not defined in ImageMagick library.' - raise WandLibraryVersionError(msg) - assertions.assert_unsigned_integer(width=width, height=height) - assertions.assert_real(number_bins=number_bins, clip_limit=clip_limit) - return library.MagickCLAHEImage(self.wand, width, height, - number_bins, clip_limit) - - @trap_exception - def clamp(self, channel=None): - """Restrict color values between 0 and quantum range. This is useful - when applying arithmetic operations that could result in color values - over/under-flowing. - - :param channel: Optional color channel. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - if channel is None: - r = library.MagickClampImage(self.wand) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickClampImageChannel(self.wand, channel_ch) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickClampImage(self.wand) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - def clone(self): - """Clones the image. It is equivalent to call :class:`Image` with - ``image`` parameter. :: - - with img.clone() as cloned: - # manipulate the cloned image - pass - - :returns: the cloned new image - :rtype: :class:`Image` - - .. versionadded:: 0.1.1 - - """ - return Image(image=self) - - @manipulative - @trap_exception - def clut(self, image, method='undefined', channel=None): - """Replace color values by referencing another image as a Color - Look Up Table. - - :param image: Color Look Up Table image. - :type image: :class:`wand.image.BaseImage` - :param method: Pixel Interpolate method. Only available with - ImageMagick-7. See :const:`PIXEL_INTERPOLATE_METHODS` - :type method: :class:`basestring` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument. - """ - if not isinstance(image, BaseImage): - raise TypeError('image must be a base image, not ' + repr(image)) - if MAGICK_VERSION_NUMBER < 0x700: - if channel is None: - r = library.MagickClutImage(self.wand, image.wand) - else: - channel_ch = self._channel_to_mask(channel) - r = library.MagickClutImageChannel(self.wand, - channel_ch, - image.wand) - else: # pragma: no cover - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - pixel_interpolate_method=method) - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - if channel is None: - r = library.MagickClutImage(self.wand, image.wand, method_idx) - else: - channel_ch = self._channel_to_mask(channel) - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickClutImage(self.wand, image.wand, method_idx) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def coalesce(self): - """Rebuilds image sequence with each frame size the same as first - frame, and composites each frame atop of previous. - - .. note:: - - Only affects GIF, and other formats with multiple pages/layers. - - .. versionadded:: 0.5.0 - """ - r = library.MagickCoalesceImages(self.wand) - if r: - self.wand = r - self.reset_sequence() - return bool(r) - - @manipulative - @trap_exception - def color_decision_list(self, ccc): - """Applies color correction from a Color Correction Collection (CCC) - xml string. An example of xml: - - .. code-block:: xml - - - - - 0.9 1.2 0.5 - 0.4 -0.5 0.6 - 1.0 0.8 1.5 - - - 0.85 - - - - - :param ccc: A XML string of the CCC contents. - :type ccc: :class:`basestring` - - .. versionadded:: 0.5.7 - """ - return library.MagickColorDecisionListImage(self.wand, binary(ccc)) - - def color_map(self, index, color=None): - """Get & Set a color at a palette index. If ``color`` is given, - the color at the index location will be set & returned. Omitting the - ``color`` argument will only return the color value at index. - - Valid indexes are between ``0`` and total :attr:`colors` of the image. - - .. note:: - - Ensure the image type is set to ``'palette'`` before calling the - :meth:`color_map` method. For example:: - - with Image(filename='graph.png') as img: - img.type = 'palette' - palette = [img.color_map(idx) for idx in range(img.colors)] - # ... - - :param index: The color postion of the image palette. - :type index: :class:`numbers.Integral` - :param color: Optional color to _set_ at the given index. - :type color: :class:`wand.color.Color` - :returns: Color at index. - :rtype: :class:`wand.color.Color` - - .. versionadded:: 0.5.3 - """ - if not isinstance(index, numbers.Integral): - raise TypeError('index most be an integer, not ' + repr(index)) - if index < 0 or index >= self.colors: - raise ValueError('index is out of palette range') - if color: - if isinstance(color, string_type): - color = Color(color) - if not isinstance(color, Color): - raise TypeError('expecting in instance of Color, not ' + - repr(color)) - with color: - r = library.MagickSetImageColormapColor(self.wand, - index, - color.resource) - if not r: # pragma: no cover - self.raise_exception() - else: - color_ptr = library.NewPixelWand() - r = library.MagickGetImageColormapColor(self.wand, - index, - color_ptr) - if not r: # pragma: no cover - color_ptr = library.DestroyPixelWand(color_ptr) - self.raise_exception() - color = Color.from_pixelwand(color_ptr) - color_ptr = library.DestroyPixelWand(color_ptr) - return color - - @manipulative - @trap_exception - def color_matrix(self, matrix): - """Adjust color values by applying a matrix transform per pixel. - - Matrix should be given as 2D list, with a max size of 6x6. - - An example of 3x3 matrix:: - - matrix = [ - [1.0, 0.0, 0.0], - [0.0, 1.0, 0.0], - [0.0, 0.0, 1.0], - ] - - Which would translate RGB color channels by calculating the - following: - - .. math:: - - \\begin{aligned} - red' &= 1.0 * red + 0.0 * green + 0.0 * blue\\\\ - green' &= 0.0 * red + 1.0 * green + 0.0 * blue\\\\ - blue' &= 0.0 * red + 0.0 * green + 1.0 * blue\\\\ - \\end{aligned} - - For RGB colorspace images, the rows & columns are laid out as: - - +---------+-----+-------+------+------+-------+--------+ - | | Red | Green | Blue | n/a | Alpha | Offset | - +=========+=====+=======+======+======+=======+========+ - | Red' | 1 | 0 | 0 | 0 | 0 | 0 | - +---------+-----+-------+------+------+-------+--------+ - | Green' | 0 | 1 | 0 | 0 | 0 | 0 | - +---------+-----+-------+------+------+-------+--------+ - | Blue' | 0 | 0 | 1 | 0 | 0 | 0 | - +---------+-----+-------+------+------+-------+--------+ - | n/a | 0 | 0 | 0 | 0 | 0 | 0 | - +---------+-----+-------+------+------+-------+--------+ - | Alpha' | 0 | 0 | 0 | 0 | 0 | 0 | - +---------+-----+-------+------+------+-------+--------+ - | Offset' | 0 | 0 | 0 | 0 | 0 | 0 | - +---------+-----+-------+------+------+-------+--------+ - - Or for a CMYK colorspace image: - - +----------+------+--------+---------+-------+-------+--------+ - | | Cyan | Yellow | Magenta | Black | Alpha | Offset | - +==========+======+========+=========+=======+=======+========+ - | Cyan' | 1 | 0 | 0 | 0 | 0 | 0 | - +----------+------+--------+---------+-------+-------+--------+ - | Yellow' | 0 | 1 | 0 | 0 | 0 | 0 | - +----------+------+--------+---------+-------+-------+--------+ - | Magenta' | 0 | 0 | 1 | 0 | 0 | 0 | - +----------+------+--------+---------+-------+-------+--------+ - | Black' | 0 | 0 | 0 | 1 | 0 | 0 | - +----------+------+--------+---------+-------+-------+--------+ - | Alpha' | 0 | 0 | 0 | 0 | 0 | 0 | - +----------+------+--------+---------+-------+-------+--------+ - | Offset' | 0 | 0 | 0 | 0 | 0 | 0 | - +----------+------+--------+---------+-------+-------+--------+ - - See `color-matrix`__ for examples. - - __ https://www.imagemagick.org/Usage/color_mods/#color-matrix - - :see: Example of :ref:`color_matrix`. - - :param matrix: 2D List of doubles. - :type matrix: :class:`collections.abc.Sequence` - - .. versionadded:: 0.5.3 - """ - if not isinstance(matrix, abc.Sequence): - raise TypeError('matrix must be a sequence, not ' + repr(matrix)) - rows = len(matrix) - columns = None - values = [] - for row in matrix: - if not isinstance(row, abc.Sequence): - raise TypeError('nested row must be a sequence, not ' + - repr(row)) - if columns is None: - columns = len(row) - elif columns != len(row): - raise ValueError('rows have different column length') - for column in row: - values.append(str(column)) - kernel = binary('{0}x{1}:{2}'.format(columns, - rows, - ','.join(values))) - exception_info = libmagick.AcquireExceptionInfo() - if MAGICK_VERSION_NUMBER < 0x700: - kernel_info = libmagick.AcquireKernelInfo(kernel) - else: # pragma: no cover - kernel_info = libmagick.AcquireKernelInfo(kernel, exception_info) - exception_info = libmagick.DestroyExceptionInfo(exception_info) - r = library.MagickColorMatrixImage(self.wand, kernel_info) - kernel_info = libmagick.DestroyKernelInfo(kernel_info) - return r - - @manipulative - @trap_exception - def color_threshold(self, start=None, stop=None): - """Forces all pixels in color range to white, and all other pixels to - black. - - .. note:: - - This method is only works with ImageMagick-7.0.10, or later. - - :param start: Color to begin color range. - :type start: :class:`wand.color.Color` - :param stop: Color to end color range. - :type stop: :class:`wand.color.Color` - - .. versionadded:: 0.6.4 - """ - if isinstance(start, string_type): - start = Color(start) - if isinstance(stop, string_type): - stop = Color(stop) - assertions.assert_color(start=start, stop=stop) - if library.MagickColorThresholdImage is None: - msg = 'Method "color_threshold" not available.' - raise WandLibraryVersionError(msg) - with start: - with stop: - r = library.MagickColorThresholdImage(self.wand, - start.resource, - stop.resource) - return r - - @manipulative - @trap_exception - def colorize(self, color=None, alpha=None): - """Blends a given fill color over the image. The amount of blend is - determined by the color channels given by the ``alpha`` argument. - - :see: Example of :ref:`colorize`. - - :param color: Color to paint image with. - :type color: :class:`wand.color.Color` - :param alpha: Defines how to blend color. - :type alpha: :class:`wand.color.Color` - - .. versionadded:: 0.5.3 - """ - if isinstance(color, string_type): - color = Color(color) - if isinstance(alpha, string_type): - alpha = Color(alpha) - assertions.assert_color(color=color, alpha=alpha) - with color: - with alpha: - r = library.MagickColorizeImage(self.wand, - color.resource, - alpha.resource) - return r - - @manipulative - @trap_exception - def combine(self, channel='rgb_channels', colorspace='rgb'): - """Creates an image where each color channel is assigned by a grayscale - image in a sequence. - - .. warning:: - - If your using ImageMagick-6, use ``channel`` argument to control - the color-channel order. With ImageMagick-7, the ``channel`` - argument has been replaced with ``colorspace``. - - For example:: - - for wand.image import Image - - with Image() as img: - img.read(filename='red_channel.png') - img.read(filename='green_channel.png') - img.read(filename='blue_channel.png') - img.combine(colorspace='rgb') - img.save(filename='output.png') - - :param channel: Determines the colorchannel ordering of the - sequence. Only used for ImageMagick-6. - See :const:`CHANNELS`. - :type channel: :class:`basestring` - :param colorspace: Determines the colorchannel ordering of the - sequence. Only used for ImageMagick-7. - See :const:`COLORSPACE_TYPES`. - :type colorspace: :class:`basestring` - - .. versionadded:: 0.5.9 - """ - assertions.string_in_list(COLORSPACE_TYPES, - 'wand.image.COLORSPACE_TYPES', - colorspace=colorspace) - library.MagickResetIterator(self.wand) - colorspace_c = COLORSPACE_TYPES.index(colorspace) - channel_c = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - new_wand = library.MagickCombineImages(self.wand, channel_c) - else: # pragma: no-cover - new_wand = library.MagickCombineImages(self.wand, colorspace_c) - if new_wand: - self.wand = new_wand - self.reset_sequence() - return bool(new_wand) - - @manipulative - def compare(self, image, metric='undefined', highlight=None, - lowlight=None): - """Compares an image with another, and returns a reconstructed - image & computed distortion. The reconstructed image will show the - differences colored with ``highlight``, and similarities with - ``lowlight``. - - If you need the computed distortion between to images without a - image being reconstructed, use :meth:`get_image_distortion()` method. - - Set :attr:`fuzz` property to adjust pixel-compare thresholds. - - For example:: - - from wand.image import Image - - with Image(filename='input.jpg') as base: - with Image(filename='subject.jpg') as img: - base.fuzz = base.quantum_range * 0.20 # Threshold of 20% - result_image, result_metric = base.compare(img) - with result_image: - result_image.save(filename='diff.jpg') - - :param image: The reference image - :type image: :class:`wand.image.Image` - :param metric: The metric type to use for comparing. See - :const:`COMPARE_METRICS` - :type metric: :class:`basestring` - :param highlight: Set the color of the delta pixels in the resulting - difference image. - :type highlight: :class:`~wand.color.Color` or :class:`basestring` - :param lowlight: Set the color of the similar pixels in the resulting - difference image. - :type lowlight: :class:`~wand.color.Color` or :class:`basestring` - :returns: The difference image(:class:`wand.image.Image`), - the computed distortion between the images - (:class:`numbers.Real`) - :rtype: :class:`tuple` ( :class:`Image`, :class:`numbers.Real` ) - - .. versionadded:: 0.4.3 - - .. versionchanged:: 0.5.3 - Added support for ``highlight`` & ``lowlight``. - """ - assertions.string_in_list(COMPARE_METRICS, - 'wand.image.COMPARE_METRICS', - metric=metric) - if highlight: - if isinstance(highlight, Color): - highlight = highlight.string - library.MagickSetImageArtifact(self.wand, - b'compare:highlight-color', - binary(highlight)) - if lowlight: - if isinstance(lowlight, Color): - lowlight = lowlight.string - library.MagickSetImageArtifact(self.wand, - b'compare:lowlight-color', - binary(lowlight)) - metric = COMPARE_METRICS.index(metric) - distortion = ctypes.c_double(0.0) - compared_image = library.MagickCompareImages(self.wand, image.wand, - metric, - ctypes.byref(distortion)) - return Image(BaseImage(compared_image)), distortion.value - - @manipulative - def complex(self, operator='undefined', snr=None): - """Performs `complex`_ mathematics against two images in a sequence, - and generates a new image with two results. - - .. seealso:: - - :meth:`forward_fourier_transform` & - :meth:`inverse_fourier_transform` - - .. code:: - - from wand.image import Image - - with Image(filename='real_part.png') as imgA: - with Image(filename='imaginary_part.png') as imgB: - imgA.sequence.append(imgB) - with imgA.complex('conjugate') as results: - results.save(filename='output-%02d.png') - - .. _complex: https://en.wikipedia.org/wiki/Complex_number - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param operator: Define which mathematic operator to perform. See - :const:`COMPLEX_OPERATORS`. - :type operator: :class:`basestring` - :param snr: Optional ``SNR`` parameter for ``'divide'`` operator. - :type snr: :class:`basestring` - :raises WandLibraryVersionError: If ImageMagick library does not - support this function. - - .. versionadded:: 0.5.5 - """ - if library.MagickComplexImages is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - assertions.string_in_list(COMPLEX_OPERATORS, - 'wand.image.COMPLEX_OPERATORS', - operator=operator) - if snr is not None: - key = b'complex:snr=float' - val = to_bytes(snr) - library.MagickSetImageArtifact(self.wand, key, val) - operator_idx = COMPLEX_OPERATORS.index(operator) - wand = library.MagickComplexImages(self.wand, operator_idx) - if not bool(wand): - self.raise_exception() - return Image(BaseImage(wand)) - - @trap_exception - def composite(self, image, left=None, top=None, operator='over', - arguments=None, gravity=None): - """Places the supplied ``image`` over the current image, with the top - left corner of ``image`` at coordinates ``left``, ``top`` of the - current image. The dimensions of the current image are not changed. - - :param image: the image placed over the current image - :type image: :class:`wand.image.Image` - :param left: the x-coordinate where `image` will be placed - :type left: :class:`numbers.Integral` - :param top: the y-coordinate where `image` will be placed - :type top: :class:`numbers.Integral` - :param operator: the operator that affects how the composite - is applied to the image. available values - can be found in the :const:`COMPOSITE_OPERATORS` - list. Default is ``'over'``. - :type operator: :class:`basestring` - :param arguments: Additional numbers given as a geometry string, or - comma delimited values. This is needed for - ``'blend'``, ``'displace'``, ``'dissolve'``, and - ``'modulate'`` operators. - :type arguments: :class:`basestring` - :param gravity: Calculate the ``top`` & ``left`` values based on - gravity value from :const:`GRAVITY_TYPES`. - :type: gravity: :class:`basestring` - - .. versionadded:: 0.2.0 - - .. versionchanged:: 0.5.3 - The operator can be set, as well as additional composite arguments. - - .. versionchanged:: 0.5.3 - Optional ``gravity`` argument was added. - """ - if top is None and left is None: - if gravity is None: - gravity = self.gravity - top, left = self._gravity_to_offset(gravity, - image.width, - image.height) - elif gravity is not None: - raise TypeError('Can not use gravity if top & left are given') - elif top is None: - top = 0 - elif left is None: - left = 0 - assertions.assert_integer(left=left, top=top) - try: - op = COMPOSITE_OPERATORS.index(operator) - except IndexError: - raise ValueError(repr(operator) + ' is an invalid composite ' - 'operator type; see wand.image.COMPOSITE_' - 'OPERATORS dictionary') - if arguments: - assertions.assert_string(arguments=arguments) - r = library.MagickSetImageArtifact(image.wand, - binary('compose:args'), - binary(arguments)) - if not r: - self.raise_exception() - r = library.MagickSetImageArtifact(self.wand, - binary('compose:args'), - binary(arguments)) - if not r: # pragma: no cover - self.raise_exception() - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickCompositeImage(self.wand, image.wand, op, - int(left), int(top)) - else: # pragma: no cover - r = library.MagickCompositeImage(self.wand, image.wand, op, True, - int(left), int(top)) - return r - - @manipulative - @trap_exception - def composite_channel(self, channel, image, operator, left=None, top=None, - arguments=None, gravity=None): - """Composite two images using the particular ``channel``. - - :param channel: the channel type. available values can be found - in the :const:`CHANNELS` mapping - :param image: the composited source image. - (the receiver image becomes the destination) - :type image: :class:`Image` - :param operator: the operator that affects how the composite - is applied to the image. available values - can be found in the :const:`COMPOSITE_OPERATORS` - list - :type operator: :class:`basestring` - :param left: the column offset of the composited source image - :type left: :class:`numbers.Integral` - :param top: the row offset of the composited source image - :type top: :class:`numbers.Integral` - :param arguments: Additional numbers given as a geometry string, or - comma delimited values. This is needed for - ``'blend'``, ``'displace'``, ``'dissolve'``, and - ``'modulate'`` operators. - :type arguments: :class:`basestring` - :param gravity: Calculate the ``top`` & ``left`` values based on - gravity value from :const:`GRAVITY_TYPES`. - :type: gravity: :class:`basestring` - :raises ValueError: when the given ``channel`` or - ``operator`` is invalid - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.5.3 - Support for optional composite arguments has been added. - - .. versionchanged:: 0.5.3 - Optional ``gravity`` argument was added. - """ - assertions.assert_string(operator=operator) - ch_const = self._channel_to_mask(channel) - if gravity: - if left is None and top is None: - top, left = self._gravity_to_offset(gravity, - image.width, - image.height) - else: - raise TypeError('Can not use gravity if top & left are given') - if top is None: - top = 0 - if left is None: - left = 0 - assertions.assert_integer(left=left, top=top) - try: - op = COMPOSITE_OPERATORS.index(operator) - except IndexError: - raise IndexError(repr(operator) + ' is an invalid composite ' - 'operator type; see wand.image.COMPOSITE_' - 'OPERATORS dictionary') - if arguments: - assertions.assert_string(arguments=arguments) - library.MagickSetImageArtifact(image.wand, - binary('compose:args'), - binary(arguments)) - library.MagickSetImageArtifact(self.wand, - binary('compose:args'), - binary(arguments)) - if library.MagickCompositeImageChannel: - r = library.MagickCompositeImageChannel(self.wand, ch_const, - image.wand, op, int(left), - int(top)) - else: # pragma: no cover - ch_mask = library.MagickSetImageChannelMask(self.wand, ch_const) - r = library.MagickCompositeImage(self.wand, image.wand, op, True, - int(left), int(top)) - library.MagickSetImageChannelMask(self.wand, ch_mask) - return r - - @manipulative - @trap_exception - def concat(self, stacked=False): - """Concatenates images in stack into a single image. Left-to-right - by default, top-to-bottom if ``stacked`` is True. - - :param stacked: stack images in a column, or in a row (default) - :type stacked: :class:`bool` - - .. versionadded:: 0.5.0 - """ - assertions.assert_bool(stacked=stacked) - r = library.MagickAppendImages(self.wand, stacked) - if r: - self.wand = r - self.reset_sequence() - return bool(r) - - def connected_components(self, **kwargs): - """Evaluates binary image, and groups connected pixels into objects. - This method will also return a list of - :class:`ConnectedComponentObject` instances that will describe an - object's features. - - .. code:: - - from wand.image import Image - - with Image(filename='objects.gif') as img: - objects = img.connected_components() - for cc_obj in objects: - print("{0._id}: {0.size} {0.offset}".format(cc_obj)) - - #=> 0: (256, 171) (0, 0) - #=> 2: (120, 135) (104, 18) - #=> 3: (50, 36) (129, 44) - #=> 4: (21, 23) (0, 45) - #=> 1: (4, 10) (252, 0) - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - .. tip:: - - Set :attr:`fuzz` property to increase pixel matching by reducing - tolerance of color-value comparisons:: - - from wand.image import Image - from wand.version import QUANTUM_RANGE - - with Image(filename='objects.gif') as img: - img.fuzz = 0.1 * QUANTUM_RANGE # 10% - objects = img.connected_components() - - :param angle_threshold: Optional argument that merges any region with - an equivalent ellipse smaller than a given - value. Requires ImageMagick-7.0.9-24, or - greater. - :type angle_threshold: :class:`basestring` - :param area_threshold: Optional argument to merge objects under an - area size. - :type area_threshold: :class:`basestring` - :param background_id: Optional argument to identify which object - should be the background. Requires - ImageMagick-7.0.9-24, or greater. - :type background_id: :class:`basestring` - :param circularity_threshold: Optional argument that merges any region - smaller than value defined as: - ``4*pi*area/perimeter^2``. Requires - ImageMagick-7.0.9-24, or greater. - :type circularity_threshold: :class:`basestring` - :param connectivity: Either ``4``, or ``8``. A value of ``4`` will - evaluate each pixels top-bottom, & left-right - neighbors. A value of ``8`` will use the same - pixels as with ``4``, but will also include the - four corners of each pixel. Default value of ``4``. - :type connectivity: :class:`numbers.Integral` - :param diameter_threshold: Optional argument to merge any region under - a given value. A region is defined as: - ``sqr(4*area/pi)``. Requires - ImageMagick-7.0.9-24. - :type diameter_threshold: :class:`basestring` - :param eccentricity_threshold: Optional argument to merge any region - with ellipse eccentricity under a given - value. Requires ImageMagick-7.0.9-24, - or greater. - :param keep: Comma separated list of object IDs to isolate, the reset - are converted to transparent. - :type keep: :class:`basestring` - :param keep_colors: Semicolon separated list of objects to keep by - their color value. Requires ImageMagick-7.0.9-24, - or greater. - :type keep_colors: :class:`basestring` - :param keep_top: Keeps only the top number of objects by area. - Requires ImageMagick-7.0.9-24, or greater. - :type keep_top: :class:`basestring` - :param major_axis_threshold: Optional argument to merge any ellipse - with a major axis smaller then given - value. Requires ImageMagick-7.0.9-24, - or greater. - :type major_axis_threshold: :class:`basestring` - :param mean_color: Optional argument. Replace object color with mean - color of the source image. - :type mean_color: :class:`bool` - :param minor_axis_threshold: Optional argument to merge any ellipse - with a minor axis smaller then given - value. Requires ImageMagick-7.0.9-24, - or greater. - :type minor_axis_threshold: :class:`basestring` - :param perimeter_threshold: Optional argument to merge any region with - a perimeter smaller than the given value. - Requires ImageMagick-7.0.9-24, or greater. - :param remove: Comma separated list of object IDs to ignore, and - convert to transparent. - :type remove: :class:`basestring` - :param remove_colors: Semicolon separated list of objects to remove - by there color. Requires ImageMagick-7.0.9-24, - or greater. - :type remove_colors: :class:`basestring` - :returns: A list of :class:`ConnectedComponentObject`. - :rtype: :class:`list` [:class:`ConnectedComponentObject`] - :raises WandLibraryVersionError: If ImageMagick library - does not support this method. - - .. versionadded:: 0.5.5 - - .. versionchanged:: 0.5.6 - Added ``mean_color``, ``keep``, & ``remove`` optional arguments. - - .. versionchanged:: 0.6.4 - Added ``angle_threshold``, ``circularity_threshold``, - ``diameter_threshold``, ``eccentricity_threshold``, - ``keep_colors``, ``major_axis_threshold``, ``minor_axis_threshold``, - ``perimeter_threshold``, and ``remove_colors`` optional arguments. - """ - angle_threshold = kwargs.get('angle_threshold', None) - area_threshold = kwargs.get('area_threshold', None) - background_id = kwargs.get('background_id', None) - circularity_threshold = kwargs.get('circularity_threshold', None) - connectivity = kwargs.get('connectivity', 4) - diameter_threshold = kwargs.get('diameter_threshold', None) - eccentricity_threshold = kwargs.get('eccentricity_threshold', None) - keep = kwargs.get('keep', None) - keep_colors = kwargs.get('keep_colors', None) - keep_top = kwargs.get('keep_top', None) - major_axis_threshold = kwargs.get('major_axis_threshold', None) - mean_color = kwargs.get('mean_color', False) - minor_axis_threshold = kwargs.get('minor_axis_threshold', None) - perimeter_threshold = kwargs.get('perimeter_threshold', None) - remove = kwargs.get('remove', None) - remove_colors = kwargs.get('remove_colors', None) - if library.MagickConnectedComponentsImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - if connectivity not in (4, 8): - raise ValueError('connectivity must be 4, or 8.') - if angle_threshold is not None: - key = b'connected-components:angle-threshold' - val = to_bytes(angle_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if area_threshold is not None: - key = b'connected-components:area-threshold' - val = to_bytes(area_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if background_id is not None: - key = b'connected-components:background-id' - val = to_bytes(background_id) - library.MagickSetImageArtifact(self.wand, key, val) - if circularity_threshold is not None: - key = b'connected-components:circularity-threshold' - val = to_bytes(circularity_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if diameter_threshold is not None: - key = b'connected-components:diameter-threshold' - val = to_bytes(diameter_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if eccentricity_threshold is not None: - key = b'connected-components:eccentricity-threshold' - val = to_bytes(eccentricity_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if keep is not None: - key = b'connected-components:keep' - val = to_bytes(keep) - library.MagickSetImageArtifact(self.wand, key, val) - if keep_colors is not None: - key = b'connected-components:keep-colors' - val = to_bytes(keep_colors) - library.MagickSetImageArtifact(self.wand, key, val) - if keep_top is not None: - key = b'connected-components:keep-top' - val = to_bytes(keep_top) - library.MagickSetImageArtifact(self.wand, key, val) - if major_axis_threshold is not None: - key = b'connected-components:major-axis-threshold' - val = to_bytes(major_axis_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if mean_color: - key = b'connected-components:mean-color' - val = b'true' - library.MagickSetImageArtifact(self.wand, key, b'true') - if minor_axis_threshold is not None: - key = b'connected-components:minor-axis-threshold' - val = to_bytes(minor_axis_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if perimeter_threshold is not None: - key = b'connected-components:perimeter-threshold' - val = to_bytes(perimeter_threshold) - library.MagickSetImageArtifact(self.wand, key, val) - if remove is not None: - key = b'connected-components:remove' - val = to_bytes(remove) - library.MagickSetImageArtifact(self.wand, key, val) - if remove_colors is not None: - key = b'connected-components:remove-colors' - val = to_bytes(remove_colors) - library.MagickSetImageArtifact(self.wand, key, val) - objects_ptr = ctypes.c_void_p(0) - CCObjectInfoStructure = CCObjectInfo - if MAGICK_VERSION_NUMBER > 0x70B: - CCObjectInfoStructure = CCObjectInfo710 - elif MAGICK_VERSION_NUMBER > 0x709: - CCObjectInfoStructure = CCObjectInfo70A - ccoi_mem_size = ctypes.sizeof(CCObjectInfoStructure) - r = library.MagickConnectedComponentsImage(self.wand, connectivity, - ctypes.byref(objects_ptr)) - objects = [] - if r and objects_ptr.value: - for i in xrange(self.colors): - temp = CCObjectInfoStructure() - src_addr = objects_ptr.value + (i * ccoi_mem_size) - ctypes.memmove(ctypes.addressof(temp), src_addr, ccoi_mem_size) - objects.append(ConnectedComponentObject(temp)) - del temp - objects_ptr = libmagick.RelinquishMagickMemory(objects_ptr) - else: - self.raise_exception() - return objects - - @manipulative - @trap_exception - def contrast(self, sharpen=True): - """Enhances the difference between lighter & darker values of the - image. Set ``sharpen`` to ``False`` to reduce contrast. - - :param sharpen: Increase, or decrease, contrast. Default is ``True`` - for increased contrast. - :type sharpen: :class:`bool` - - .. versionadded:: 0.5.7 - """ - assertions.assert_bool(sharpen=sharpen) - return library.MagickContrastImage(self.wand, sharpen) - - @manipulative - @trap_exception - def contrast_stretch(self, black_point=0.0, white_point=None, - channel=None): - """Enhance contrast of image by adjusting the span of the available - colors. - - :param black_point: black point between 0.0 and 1.0. default is 0.0 - :type black_point: :class:`numbers.Real` - :param white_point: white point between 0.0 and 1.0. - Defaults to the same value given to the - ``black_point`` argument. - :type white_point: :class:`numbers.Real` - :param channel: optional color channel to apply contrast stretch - :type channel: :const:`CHANNELS` - :raises ValueError: if ``channel`` is not in :const:`CHANNELS` - - .. versionadded:: 0.4.1 - - .. versionchanged:: 0.5.5 - The ``white_point`` argument will now default to the value given - by the ``black_point`` argument. - """ - assertions.assert_real(black_point=black_point) - # If only black-point is given, match CLI behavior by - # calculating white point - if white_point is None: - white_point = black_point - assertions.assert_real(white_point=white_point) - contrast_range = float(self.width * self.height) - if 0.0 < black_point <= 1.0: - black_point *= contrast_range - if 0.0 < white_point <= 1.0: - white_point *= contrast_range - white_point = contrast_range - white_point - if channel is None: - r = library.MagickContrastStretchImage(self.wand, - black_point, - white_point) - else: - ch_const = self._channel_to_mask(channel) - if library.MagickContrastStretchImageChannel: - r = library.MagickContrastStretchImageChannel(self.wand, - ch_const, - black_point, - white_point) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickContrastStretchImage(self.wand, - black_point, - white_point) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - def convex_hull(self, background=None): - """Find the smallest convex polygon, and returns a list of points. - - .. note:: Requires ImageMagick-7.0.10 or greater. - - You can pass the list of points directly to - :meth:`Drawing.polygon() ` method - to draw the convex hull shape on the image. - - .. code:: - - from wand.image import Image - from wand.drawing import Drawing - - with Image(filename='kdf_black.png') as img: - points = img.convex_hull() - with Drawing() as ctx: - ctx.fill_color = 'transparent' - ctx.stroke_color = 'red' - ctx.polygon(points=points) - ctx(img) - img.save(filename='kdf_black_convex_hull.png') - - .. image:: ../_images/wand/image/kdf_black.png - .. image:: ../_images/wand/image/kdf_black_convex_hull.png - - :param background: Define which color value to evaluate as the - background. - :type background: :class:`basestring` or :class:`~wand.color.Color` - :returns: list of points - :rtype: :class:`list` [ :class:`tuple` ( :class:`float`, - :class:`float` ) ] - - .. versionadded:: 0.6.4 - """ - r = [] - if MAGICK_VERSION_NUMBER < 0x70A: - msg = 'ImageMagick-7.0.10 is required to use convex_hull().' - raise WandLibraryVersionError(msg) - with self.clone() as tmp: - if background is not None: - if isinstance(background, Color): - background = background.string - assertions.assert_string(background=background) - key = b'convex-hull:background-color' - val = to_bytes(background) - library.MagickSetImageArtifact(tmp.wand, key, val) - library.MagickSetOption(tmp.wand, b'format', b'%[convex-hull]') - library.MagickSetImageFormat(tmp.wand, b'INFO') - length = ctypes.c_size_t() - blob_p = library.MagickGetImageBlob(tmp.wand, - ctypes.byref(length)) - if blob_p: - blob = ctypes.string_at(blob_p, length.value) - blob_p = library.MagickRelinquishMemory(blob_p) - pts = blob.decode('ascii', 'ignore').strip().split(' ') - r = [tuple(map(lambda x: float(x), p.split(','))) for p in pts] - else: - self.raise_exception() - return r - - @manipulative - @trap_exception - def crop(self, left=0, top=0, right=None, bottom=None, - width=None, height=None, reset_coords=True, - gravity=None): - """Crops the image in-place. - - .. sourcecode:: text - - +--------------------------------------------------+ - | ^ ^ | - | | | | - | top | | - | | | | - | v | | - | <-- left --> +-------------------+ bottom | - | | ^ | | | - | | <-- width --|---> | | | - | | height | | | - | | | | | | - | | v | | | - | +-------------------+ v | - | <--------------- right ----------> | - +--------------------------------------------------+ - - :param left: x-offset of the cropped image. default is 0 - :type left: :class:`numbers.Integral` - :param top: y-offset of the cropped image. default is 0 - :type top: :class:`numbers.Integral` - :param right: second x-offset of the cropped image. - default is the :attr:`width` of the image. - this parameter and ``width`` parameter are exclusive - each other - :type right: :class:`numbers.Integral` - :param bottom: second y-offset of the cropped image. - default is the :attr:`height` of the image. - this parameter and ``height`` parameter are exclusive - each other - :type bottom: :class:`numbers.Integral` - :param width: the :attr:`width` of the cropped image. - default is the :attr:`width` of the image. - this parameter and ``right`` parameter are exclusive - each other - :type width: :class:`numbers.Integral` - :param height: the :attr:`height` of the cropped image. - default is the :attr:`height` of the image. - this parameter and ``bottom`` parameter are exclusive - each other - :type height: :class:`numbers.Integral` - :param reset_coords: - optional flag. If set, after the rotation, the coordinate frame - will be relocated to the upper-left corner of the new image. - By default is `True`. - :type reset_coords: :class:`bool` - :param gravity: optional flag. If set, will calculate the :attr:`top` - and :attr:`left` attributes. This requires both - :attr:`width` and :attr:`height` parameters to be - included. - :type gravity: :const:`GRAVITY_TYPES` - :raises ValueError: when one or more arguments are invalid - - .. note:: - - If you want to crop the image but not in-place, use slicing - operator. - - .. versionchanged:: 0.4.1 - Added ``gravity`` option. Using ``gravity`` along with - ``width`` & ``height`` to auto-adjust ``left`` & ``top`` - attributes. - - .. versionchanged:: 0.1.8 - Made to raise :exc:`~exceptions.ValueError` instead of - :exc:`~exceptions.IndexError` for invalid ``width``/``height`` - arguments. - - .. versionadded:: 0.1.7 - - """ - if not (right is None or width is None): - raise TypeError('parameters right and width are exclusive each ' - 'other; use one at a time') - elif not (bottom is None or height is None): - raise TypeError('parameters bottom and height are exclusive each ' - 'other; use one at a time') - - def abs_(n, m, null=None): - if n is None: - return m if null is None else null - elif not isinstance(n, numbers.Integral): - raise TypeError('expected integer, not ' + repr(n)) - elif n > m: - raise ValueError(repr(n) + ' > ' + repr(m)) - return m + n if n < 0 else n - - # Define left & top if gravity is given. - if gravity: - if width is None or height is None: - raise TypeError( - 'both width and height must be defined with gravity' - ) - top, left = self._gravity_to_offset(gravity, width, height) - else: - left = abs_(left, self.width, 0) - top = abs_(top, self.height, 0) - - if width is None: - right = abs_(right, self.width) - width = right - left - if height is None: - bottom = abs_(bottom, self.height) - height = bottom - top - assertions.assert_counting_number(width=width, height=height) - if ( - left == top == 0 and - width == self.width and - height == self.height - ): - return True - if self.animation: - self.wand = library.MagickCoalesceImages(self.wand) - self.reset_sequence() - library.MagickSetLastIterator(self.wand) - n = library.MagickGetIteratorIndex(self.wand) - library.MagickResetIterator(self.wand) - for i in xrange(0, n + 1): - library.MagickSetIteratorIndex(self.wand, i) - r = library.MagickCropImage(self.wand, - width, height, - left, top) - if reset_coords: - self.reset_coords() - else: - r = library.MagickCropImage(self.wand, width, height, left, top) - if reset_coords: - self.reset_coords() - return r - - @trap_exception - def cycle_color_map(self, offset=1): - """Shift the image color-map by a given offset. - - :param offset: number of steps to rotate index by. - :type offset: :class:`numbers.Integral` - - .. versionadded:: 0.5.3 - """ - assertions.assert_integer(offset=offset) - return library.MagickCycleColormapImage(self.wand, offset) - - @manipulative - @trap_exception - def decipher(self, passphrase): - """Decrypt ciphered pixels into original values. - - .. note:: - - :class:`~wand.exceptions.ImageError` will be thrown if the system's - ImageMagick library was compiled without cipher support. - - :param passphrase: the secret passphrase to decrypt with. - :type passphrase: :class:`basestring` - - .. versionadded:: 0.6.3 - """ - assertions.assert_string(passphrase=passphrase) - return library.MagickDecipherImage(self.wand, binary(passphrase)) - - @manipulative - @trap_exception - def deconstruct(self): - """Iterates over internal image stack, and adjust each frame size to - minimum bounding region of any changes from the previous frame. - - .. versionadded:: 0.5.0 - """ - r = library.MagickDeconstructImages(self.wand) - if r: - self.wand = r - self.reset_sequence() - return bool(r) - - @manipulative - @trap_exception - def deskew(self, threshold): - """Attempts to remove skew artifacts common with most - scanning & optical import devices. - - :params threshold: limit between foreground & background. Use a real - number between `0.0` & `1.0` to match CLI's percent - argument. - :type threshold: :class:`numbers.Real` - - .. versionadded:: 0.5.0 - """ - assertions.assert_real(threshold=threshold) - if 0 < threshold <= 1.0: - threshold *= self.quantum_range - return library.MagickDeskewImage(self.wand, threshold) - - @manipulative - @trap_exception - def despeckle(self): - """Applies filter to reduce noise in image. - - :see: Example of :ref:`despeckle`. - - .. versionadded:: 0.5.0 - """ - return library.MagickDespeckleImage(self.wand) - - @manipulative - @trap_exception - def distort(self, method, arguments, best_fit=False, filter=None): - """Distorts an image using various distorting methods. - - .. code:: python - - from wand.image import Image - from wand.color import Color - - with Image(filename='checks.png') as img: - img.virtual_pixel = 'background' - img.background_color = Color('green') - img.matte_color = Color('skyblue') - arguments = (0, 0, 20, 60, - 90, 0, 70, 63, - 0, 90, 5, 83, - 90, 90, 85, 88) - img.distort('perspective', arguments) - img.save(filename='checks_perspective.png') - - .. image:: ../_images/wand/image/checks.png - .. image:: ../_images/wand/image/checks_perspective.png - - Use :attr:`virtual_pixel`, :attr:`background_color`, and - :attr:`matte_color` properties to control the behavior of pixels - rendered outside of the image boundaries. - - Use :attr:`interpolate_method` to control how images scale-up. - - Distortion viewport, and scale, can be defined by using - :attr:`Image.artifacts` dictionary. For example:: - - img.artifacts['distort:viewport'] = '44x44+15+0' - img.artifacts['distort:scale'] = '10' - - :see: Additional examples of :ref:`distort`. - - :param method: Distortion method name from :const:`DISTORTION_METHODS` - :type method: :class:`basestring` - :param arguments: List of distorting float arguments - unique to distortion method - :type arguments: :class:`collections.abc.Sequence` - :param best_fit: Attempt to resize resulting image fit distortion. - Defaults False - :type best_fit: :class:`bool` - :param filter: Optional resampling filter used when calculating - pixel-value. Defaults to ``'mitchell'``, or - ``'lanczos'`` based on image type & operation. - :type filter: :class:`basestring` - - .. versionadded:: 0.4.1 - .. versionchanged:: 0.6.11 - Included `filter=` parameter. - """ - assertions.string_in_list(DISTORTION_METHODS, - 'wand.image.DISTORTION_METHODS', - method=method) - if not isinstance(arguments, abc.Sequence): - raise TypeError('expected sequence of doubles, not ' + - repr(arguments)) - argc = len(arguments) - argv = (ctypes.c_double * argc)(*arguments) - method_idx = DISTORTION_METHODS.index(method) - if filter is not None: - assertions.string_in_list(FILTER_TYPES, - 'wand.image.FILTER_TYPES', - filter=filter) - ok = False - if library.MagickSetImageFilter: - filter_idx = FILTER_TYPES.index(filter) - ok = library.MagickSetImageFilter(self.wand, - filter_idx) - else: - img_info_ptr = libmagick.AcquireImageInfo() - exp_ptr = libmagick.AcquireExceptionInfo() - img_ptr = library.GetImageFromMagickWand(self.wand) - if all([img_info_ptr, exp_ptr, img_ptr]): - libmagick.SetImageOption(img_info_ptr, - b'filter', - filter.encode()) - ok = libmagick.SyncImageSettings(img_info_ptr, - img_ptr, - exp_ptr) - if img_info_ptr: - img_info_ptr = libmagick.DestroyImageInfo(img_info_ptr) - if exp_ptr: - exp_ptr = libmagick.DestroyExceptionInfo(exp_ptr) - if not ok: - raise AttributeError('Unable to set filter for ' + - filter) - return library.MagickDistortImage(self.wand, method_idx, - argc, argv, bool(best_fit)) - - @manipulative - @trap_exception - def edge(self, radius=0.0): - """Applies convolution filter to detect edges. - - :see: Example of :ref:`edge`. - - :param radius: aperture of detection filter. - :type radius: :class:`numbers.Real` - - .. versionadded:: 0.5.0 - """ - assertions.assert_real(radius=radius) - return library.MagickEdgeImage(self.wand, radius) - - @manipulative - @trap_exception - def emboss(self, radius=0.0, sigma=0.0): - """Applies convolution filter against Gaussians filter. - - .. note:: - - The `radius` value should be larger than `sigma` for best results. - - :see: Example of :ref:`emboss`. - - :param radius: filter aperture size. - :type radius: :class:`numbers.Real` - :param sigma: standard deviation. - :type sigma: :class:`numbers.Real` - - .. versionadded:: 0.5.0 - """ - assertions.assert_real(radius=radius, sigma=sigma) - return library.MagickEmbossImage(self.wand, radius, sigma) - - @manipulative - @trap_exception - def encipher(self, passphrase): - """Encrypt plain pixels into ciphered values. - - .. note:: - - :class:`~wand.exceptions.ImageError` will be thrown if the system's - ImageMagick library was compiled without cipher support. - - :param passphrase: the secret passphrase to encrypt with. - :type passphrase: :class:`basestring` - - .. versionadded:: 0.6.3 - .. versionchanged:: 0.6.8 - Fixed C-API call. - """ - assertions.assert_string(passphrase=passphrase) - return library.MagickEncipherImage(self.wand, binary(passphrase)) - - @manipulative - @trap_exception - def enhance(self): - """Applies digital filter to reduce noise. - - :see: Example of :ref:`enhance`. - - .. versionadded:: 0.5.0 - """ - return library.MagickEnhanceImage(self.wand) - - @manipulative - @trap_exception - def equalize(self, channel=None): - """Equalizes the image histogram - - :param channel: Optional channel. See :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.3.10 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument. - """ - if channel is None: - r = library.MagickEqualizeImage(self.wand) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickEqualizeImageChannel(self.wand, channel_ch) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickEqualizeImage(self.wand) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def evaluate(self, operator=None, value=0.0, channel=None): - """Apply arithmetic, relational, or logical expression to an image. - - Percent values must be calculated against the quantum range of the - image:: - - fifty_percent = img.quantum_range * 0.5 - img.evaluate(operator='set', value=fifty_percent) - - :see: Example of :ref:`evaluate`. - - :param operator: Type of operation to calculate - :type operator: :const:`EVALUATE_OPS` - :param value: Number to calculate with ``operator`` - :type value: :class:`numbers.Real` - :param channel: Optional channel to apply operation on. - :type channel: :const:`CHANNELS` - :raises TypeError: When ``value`` is not numeric. - :raises ValueError: When ``operator``, or ``channel`` are not defined - in constants. - - .. versionadded:: 0.4.1 - """ - assertions.string_in_list(EVALUATE_OPS, 'wand.image.EVALUATE_OPS', - operator=operator) - assertions.assert_real(value=value) - idx_op = EVALUATE_OPS.index(operator) - if channel is None: - r = library.MagickEvaluateImage(self.wand, idx_op, value) - else: - ch_const = self._channel_to_mask(channel) - # Use channel method if IM6, else create channel mask for IM7. - if library.MagickEvaluateImageChannel: - r = library.MagickEvaluateImageChannel(self.wand, - ch_const, - idx_op, - value) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickEvaluateImage(self.wand, idx_op, value) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - def export_pixels(self, x=0, y=0, width=None, height=None, - channel_map="RGBA", storage='char'): - """Export pixel data from a raster image to - a list of values. - - The ``channel_map`` tells ImageMagick which color - channels to export, and what order they should be - written as -- per pixel. Valid entries for - ``channel_map`` are: - - - ``'R'`` - Red channel - - ``'G'`` - Green channel - - ``'B'`` - Blue channel - - ``'A'`` - Alpha channel (``0`` is transparent) - - ``'O'`` - Alpha channel (``0`` is opaque) - - ``'C'`` - Cyan channel - - ``'Y'`` - Yellow channel - - ``'M'`` - Magenta channel - - ``'K'`` - Black channel - - ``'I'`` - Intensity channel (only for grayscale) - - ``'P'`` - Padding - - See :const:`STORAGE_TYPES` for a list of valid - ``storage`` options. This tells ImageMagick - what type of data it should calculate & write to. - For example; a storage type of ``'char'`` will write - a 8-bit value between 0 ~ 255, a storage type - of ``'short'`` will write a 16-bit value between - 0 ~ 65535, and a ``'integer'`` will write a - 32-bit value between 0 ~ 4294967295. - - .. note:: - - By default, the entire image will be exported - as ``'char'`` storage with each pixel mapping - Red, Green, Blue, & Alpha channels. - - - :param x: horizontal starting coordinate of raster. - :type x: :class:`numbers.Integral` - :param y: vertical starting coordinate of raster. - :type y: :class:`numbers.Integral` - :param width: horizontal length of raster. - :type width: :class:`numbers.Integral` - :param height: vertical length of raster. - :type height: :class:`numbers.Integral` - :param channel_map: a string listing the channel data - format for each pixel. - :type channel_map: :class:`basestring` - :param storage: what data type each value should - be calculated as. - :type storage: :class:`basestring` - :returns: list of values. - :rtype: :class:`collections.abc.Sequence` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.6.11 - Update storage type size for `"long"` & `"quantum"` values. - """ - _w, _h = self.size - if width is None: - width = _w - if height is None: - height = _h - assertions.assert_integer(x=x, y=y, width=width, height=height) - assertions.assert_string(channel_map=channel_map) - assertions.string_in_list(STORAGE_TYPES, 'wand.image.STORAGE_TYPES', - storage=storage) - channel_map = channel_map.upper() - valid_channels = 'RGBAOCYMKIP' - for channel in channel_map: - if channel not in valid_channels: - raise ValueError('Unknown channel label: ' + - repr(channel)) - c_storage_types = [ - None, # undefined - ctypes.c_ubyte, # char - ctypes.c_double, # double - ctypes.c_float, # float - ctypes.c_uint, # integer - ctypes.c_uint64, # long - library.PixelGetRedQuantum.restype, # quantum - ctypes.c_ushort # short - ] - s_index = STORAGE_TYPES.index(storage) - c_storage = c_storage_types[s_index] - total_pixels = width * height - c_buffer_size = total_pixels * len(channel_map) - c_buffer = (c_buffer_size * c_storage)() - r = library.MagickExportImagePixels(self.wand, - x, y, width, height, - binary(channel_map), - s_index, - ctypes.byref(c_buffer)) - if not r: # pragma: no cover - self.raise_exception() - return c_buffer[:c_buffer_size] - - @manipulative - @trap_exception - def extent(self, width=None, height=None, x=None, y=None, gravity=None): - """Adjust the canvas size of the image. Use ``x`` & ``y`` to offset - the image's relative placement in the canvas, or ``gravity`` helper - for quick position placement. - - :param width: the target width of the extended image. - Default is the :attr:`width` of the image. - :type width: :class:`numbers.Integral` - :param height: the target height of the extended image. - Default is the :attr:`height` of the image. - :type height: :class:`numbers.Integral` - :param x: the x-axis offset of the extended image. - Default is 0, and can not be used with ``gravity``. - :type x: :class:`numbers.Integral` - :param y: the :attr:`y` offset of the extended image. - Default is 0, and can not be used with ``gravity``. - :type y: :class:`numbers.Integral` - :param gravity: position of the item extent when not using ``x`` & - ``y``. See :const:`GRAVITY_TYPES`. - :type gravity: :class:`basestring` - - .. versionadded:: 0.4.5 - - .. versionchanged:: 0.6.8 - Added ``gravity`` argument. - """ - if width is None or width == 0: - width = self.width - if height is None or height == 0: - height = self.height - assertions.assert_unsigned_integer(width=width, height=height) - if gravity is None: - if x is None: - x = 0 - if y is None: - y = 0 - else: - if x is not None or y is not None: - raise ValueError('x & y can not be used with gravity.') - y, x = self._gravity_to_offset(gravity, width, height) - assertions.assert_integer(x=x, y=y) - return library.MagickExtentImage(self.wand, width, height, x, y) - - def features(self, distance): - """Calculate directional image features for each color channel. - Feature metrics including: - - - angular second moment - - contrast - - correlation - - variance sum of squares - - inverse difference moment - - sum average - - sum variance - - sum entropy - - entropy - - difference variance - - difference entropy - - information measures of correlation 1 - - information measures of correlation 2 - - maximum correlation coefficient - - With each metric containing horizontal, vertical, left & right - diagonal values. - - .. code:: - - from wand.image import Image - - with Image(filename='rose:') as img: - channel_features = img.features(distance=32) - for channels, features in channel_features.items(): - print(channels) - for feature, directions in features.items(): - print(' ', feature) - for name, value in directions.items(): - print(' ', name, value) - - :param distance: Define the distance if pixels to calculate. - :type distance: :class:`numbers.Integral` - :returns: a dict mapping each color channel with a dict of each - feature. - :rtype: :class:`dict` - - .. versionadded:: 0.5.5 - """ - def build_channel(address, channel): - feature = ChannelFeature() - size = ctypes.sizeof(feature) - ctypes.memmove(ctypes.addressof(feature), - feature_ptr + (CHANNELS[channel] * size), - size) - keys = ('horizontal', 'vertical', - 'left_diagonal', 'right_diagonal') - feature_dict = {} - for k in feature._fields_: - a = k[0] - feature_dict[a] = dict(zip(keys, getattr(feature, a))) - return feature_dict - if MAGICK_VERSION_NUMBER < 0x700: - method = library.MagickGetImageChannelFeatures - else: # pragma: no cover - method = library.MagickGetImageFeatures - assertions.assert_unsigned_integer(distance=distance) - feature_ptr = method(self.wand, distance) - response = {} - if feature_ptr: - colorspace = self.colorspace - if self.alpha_channel: - response['alpha'] = build_channel(feature_ptr, 'alpha') - if colorspace == 'gray': - response['gray'] = build_channel(feature_ptr, 'gray') - elif colorspace == 'cmyk': - response['cyan'] = build_channel(feature_ptr, 'cyan') - response['magenta'] = build_channel(feature_ptr, 'magenta') - response['yellow'] = build_channel(feature_ptr, 'yellow') - response['black'] = build_channel(feature_ptr, 'black') - else: - response['red'] = build_channel(feature_ptr, 'red') - response['green'] = build_channel(feature_ptr, 'green') - response['blue'] = build_channel(feature_ptr, 'blue') - feature_ptr = library.MagickRelinquishMemory(feature_ptr) - return response - - def fft(self, magnitude=True): - """Alias for :meth:`forward_fourier_transform`. - - .. versionadded:: 0.5.7 - """ - return self.forward_fourier_transform(magnitude) - - @manipulative - @trap_exception - def flip(self): - """Creates a vertical mirror image by reflecting the pixels around - the central x-axis. It manipulates the image in place. - - :see: Example of :ref:`flip_flop`. - - .. versionadded:: 0.3.0 - - """ - return library.MagickFlipImage(self.wand) - - @manipulative - @trap_exception - def flop(self): - """Creates a horizontal mirror image by reflecting the pixels around - the central y-axis. It manipulates the image in place. - - :see: Example of :ref:`flip_flop`. - - .. versionadded:: 0.3.0 - - """ - return library.MagickFlopImage(self.wand) - - @trap_exception - def forward_fourier_transform(self, magnitude=True): - """Performs a discrete Fourier transform. The image stack is replaced - with the results. Either a pair of magnitude & phase images, or - real & imaginary (HDRI). - - .. code:: - - from wand.image import Image - from wand.version import QUANTUM_RANGE - - with Image(filename='source.png') as img: - img.forward_fourier_transform() - img.depth = QUANTUM_RANGE - img.save(filename='fft_%02d.png') - - .. seealso:: :meth:`inverse_fourier_transform` & :meth:`complex` - - .. note:: - - ImageMagick must have HDRI support to compute real & imaginary - components (i.e. ``magnitude=False``). - - :param magnitude: If ``True``, generate magnitude & phase, else - real & imaginary. Default ``True`` - :type magnitude: :class:`bool` - - .. versionadded:: 0.5.5 - """ - assertions.assert_bool(magnitude=magnitude) - return library.MagickForwardFourierTransformImage(self.wand, magnitude) - - @manipulative - @trap_exception - def frame(self, matte=None, width=1, height=1, inner_bevel=0, - outer_bevel=0, compose='over'): - """Creates a bordered frame around image. - Inner & outer bevel can simulate a 3D effect. - - :param matte: color of the frame - :type matte: :class:`wand.color.Color` - :param width: total size of frame on x-axis - :type width: :class:`numbers.Integral` - :param height: total size of frame on y-axis - :type height: :class:`numbers.Integral` - :param inner_bevel: inset shadow length - :type inner_bevel: :class:`numbers.Real` - :param outer_bevel: outset highlight length - :type outer_bevel: :class:`numbers.Real` - :param compose: Optional composite operator. Default ``'over'``, and - only available with ImageMagick-7. - :type compose: :class:`basestring` - - .. versionadded:: 0.4.1 - - .. versionchanged:: 0.5.6 - Added optional ``compose`` parameter. - """ - if matte is None: - matte = Color('gray') - if isinstance(matte, string_type): - matte = Color(matte) - assertions.assert_color(matte=matte) - assertions.assert_integer(width=width, height=height) - assertions.assert_real(inner_bevel=inner_bevel, - outer_bevel=outer_bevel) - with matte: - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickFrameImage(self.wand, - matte.resource, - width, height, - inner_bevel, outer_bevel) - else: # pragma: no cover - assertions.string_in_list(COMPOSITE_OPERATORS, - 'wand.image.COMPOSITE_OPERATORS', - compose=compose) - op = COMPOSITE_OPERATORS.index(compose) - r = library.MagickFrameImage(self.wand, - matte.resource, - width, height, - inner_bevel, outer_bevel, - op) - return r - - @manipulative - @trap_exception - def function(self, function, arguments, channel=None): - """Apply an arithmetic, relational, or logical expression to an image. - - Defaults entire image, but can isolate affects to single color channel - by passing :const:`CHANNELS` value to ``channel`` parameter. - - .. note:: - - Support for function methods added in the following versions - of ImageMagick. - - - ``'polynomial'`` >= 6.4.8-8 - - ``'sinusoid'`` >= 6.4.8-8 - - ``'arcsin'`` >= 6.5.3-1 - - ``'arctan'`` >= 6.5.3-1 - - :see: Example of :ref:`function`. - - :param function: a string listed in :const:`FUNCTION_TYPES` - :type function: :class:`basestring` - :param arguments: a sequence of doubles to apply against ``function`` - :type arguments: :class:`collections.abc.Sequence` - :param channel: optional :const:`CHANNELS`, defaults all - :type channel: :class:`basestring` - :raises ValueError: when a ``function``, or ``channel`` is not - defined in there respected constant - :raises TypeError: if ``arguments`` is not a sequence - - .. versionadded:: 0.4.1 - """ - assertions.string_in_list(FUNCTION_TYPES, 'wand.image.FUNCTION_TYPES', - function=function) - if not isinstance(arguments, abc.Sequence): - raise TypeError('expecting sequence of arguments, not ' + - repr(arguments)) - argc = len(arguments) - argv = (ctypes.c_double * argc)(*arguments) - index = FUNCTION_TYPES.index(function) - if channel is None: - r = library.MagickFunctionImage(self.wand, index, argc, argv) - else: - ch_channel = self._channel_to_mask(channel) - # Use channel method if IM6, else create channel mask for IM7. - if library.MagickFunctionImageChannel: - r = library.MagickFunctionImageChannel(self.wand, - ch_channel, - index, - argc, - argv) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_channel) - r = library.MagickFunctionImage(self.wand, index, argc, argv) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - def fx(self, expression, channel=None): - """Manipulate each pixel of an image by given expression. - - FX will preserver current wand instance, and return a new instance of - :class:`Image` containing affected pixels. - - Defaults entire image, but can isolate affects to single color channel - by passing :const:`CHANNELS` value to ``channel`` parameter. - - .. seealso:: The anatomy of FX expressions can be found at - http://www.imagemagick.org/script/fx.php - - - :see: Example of :ref:`fx`. - - :param expression: The entire FX expression to apply - :type expression: :class:`basestring` - :param channel: Optional channel to target. - :type channel: :const:`CHANNELS` - :returns: A new instance of an image with expression applied - :rtype: :class:`Image` - - .. versionadded:: 0.4.1 - - .. versionchanged:: 0.6.9 - Will raise :class:`WandRuntimeError` if method is unable to generate - a new image & doesn't throw an exception. - """ - assertions.assert_string(expression=expression) - c_expression = binary(expression) - if channel is None: - new_wand = library.MagickFxImage(self.wand, c_expression) - else: - ch_channel = self._channel_to_mask(channel) - if library.MagickFxImageChannel: - new_wand = library.MagickFxImageChannel(self.wand, - ch_channel, - c_expression) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_channel) - new_wand = library.MagickFxImage(self.wand, c_expression) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - if new_wand: - return Image(image=BaseImage(new_wand)) - else: # pragma: no cover - self.raise_exception() - # If no exception is on the stack, then raise a generic run-time - # error. This can happen naturally if the source image is null, - # or the FX expression was unable to generate a new raster. - raise WandRuntimeError('No FX Image was generated by expression.') - - @manipulative - @trap_exception - def gamma(self, adjustment_value=1.0, channel=None): - """Gamma correct image. - - Specific color channels can be correct individual. Typical values - range between 0.8 and 2.3. - - :see: Example of :ref:`gamma`. - - :param adjustment_value: value to adjust gamma level. Default `1.0` - :type adjustment_value: :class:`numbers.Real` - :param channel: optional channel to apply gamma correction - :type channel: :class:`basestring` - :raises TypeError: if ``gamma_point`` is not a :class:`numbers.Real` - :raises ValueError: if ``channel`` is not in :const:`CHANNELS` - - .. versionadded:: 0.4.1 - - """ - assertions.assert_real(adjustment_value=adjustment_value) - if channel is None: - r = library.MagickGammaImage(self.wand, adjustment_value) - else: - ch_const = self._channel_to_mask(channel) - if library.MagickGammaImageChannel: - r = library.MagickGammaImageChannel(self.wand, - ch_const, - adjustment_value) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickGammaImage(self.wand, adjustment_value) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def gaussian_blur(self, radius=0.0, sigma=0.0, channel=None): - """Blurs the image. We convolve the image with a gaussian operator - of the given ``radius`` and standard deviation (``sigma``). - For reasonable results, the ``radius`` should be larger - than ``sigma``. Use a ``radius`` of 0 and :meth:`blur()` selects - a suitable ``radius`` for you. - - :see: Example of :ref:`gaussian_blur`. - - :param radius: the radius of the, in pixels, - not counting the center pixel - :type radius: :class:`numbers.Real` - :param sigma: the standard deviation of the, in pixels - :type sigma: :class:`numbers.Real` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.3.3 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - .. versionchanged:: 0.5.7 - Positional arguments ``radius`` & ``sigma`` have been converted - to keyword arguments. - """ - assertions.assert_real(radius=radius, sigma=sigma) - if channel is None: - r = library.MagickGaussianBlurImage(self.wand, radius, sigma) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickGaussianBlurImageChannel(self.wand, - channel_ch, - radius, - sigma) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickGaussianBlurImage(self.wand, radius, sigma) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - def get_image_distortion(self, image, metric='undefined'): - """Compares two images, and return the specified distortion metric. - - This method is faster than :meth:`compare()` method as ImageMagick - will not need to reconstruct an image. - - :param image: Image to reference. - :type image: :class:`wand.image.BaseImage` - :param metric: Compare disortion metric to use. See - :const:`COMPARE_METRICS`. - :type metric: :class:`basestring` - :returns: Computed value of the distortion metric used. - :rtype: :class:`numbers.Real` - - .. versionadded:: 0.6.6 - """ - if not isinstance(image, BaseImage): - raise TypeError('expecting a base image, not ' + repr(image)) - assertions.string_in_list(COMPARE_METRICS, - 'wand.image.COMPARE_METRICS', - metric=metric) - metric_idx = COMPARE_METRICS.index(metric) - dist = ctypes.c_double(0.0) - ok = library.MagickGetImageDistortion(self.wand, image.wand, - metric_idx, dist) - if not ok: - self.raise_exception() - return dist.value - - @manipulative - @trap_exception - def hald_clut(self, image, channel=None): - """Replace color values by referencing a Higher And Lower Dimension - (HALD) Color Look Up Table (CLUT). You can generate a HALD image - by using ImageMagick's `hald:` protocol. :: - - with Image(filename='rose:') as img: - with Image(filename='hald:3') as hald: - hald.gamma(1.367) - img.hald_clut(hald) - - :param image: The HALD color matrix. - :type image: :class:`wand.image.BaseImage` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - if not isinstance(image, BaseImage): - raise TypeError('expecting a base image, not ' + repr(image)) - if channel is None: - r = library.MagickHaldClutImage(self.wand, image.wand) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickHaldClutImageChannel(self.wand, channel_ch, - image.wand) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickHaldClutImage(self.wand, image.wand) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def hough_lines(self, width, height=None, threshold=40): - """Identify lines within an image. Use :meth:`canny` to reduce image - to a binary edge before calling this method. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param width: Local maxima of neighboring pixels. - :type width: :class:`numbers.Integral` - :param height: Local maxima of neighboring pixels. - :type height: :class:`numbers.Integral` - :param threshold: Line count to limit. Default to 40. - :type threshold: :class:`numbers.Integral` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickHoughLineImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - if height is None: - height = width - assertions.assert_unsigned_integer(width=width, height=height, - threshold=threshold) - return library.MagickHoughLineImage(self.wand, width, height, - threshold) - - def ift(self, phase, magnitude=True): - """Alias for :meth:`inverse_fourier_transform`. - - .. versionadded:: 0.5.7 - """ - return self.inverse_fourier_transform(phase, magnitude) - - @trap_exception - def implode(self, amount=0.0, method="undefined"): - """Creates a "imploding" effect by pulling pixels towards the center - of the image. - - :see: Example of :ref:`implode`. - - :param amount: Normalized degree of effect between `0.0` & `1.0`. - :type amount: :class:`numbers.Real` - :param method: Which interpolate method to apply to effected pixels. - See :const:`PIXEL_INTERPOLATE_METHODS` for a list of - options. Only available with ImageMagick-7. - :type method: :class:`basestring` - - .. versionadded:: 0.5.2 - """ - assertions.assert_real(amount=amount) - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - method=method) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickImplodeImage(self.wand, amount) - else: # pragma: no cover - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - r = library.MagickImplodeImage(self.wand, amount, method_idx) - return r - - @trap_exception - def import_pixels(self, x=0, y=0, width=None, height=None, - channel_map='RGB', storage='char', data=None): - """Import pixel data from a byte-string to - the image. The instance of :class:`Image` must already - be allocated with the correct size. - - The ``channel_map`` tells ImageMagick which color - channels to export, and what order they should be - written as -- per pixel. Valid entries for - ``channel_map`` are: - - - ``'R'`` - Red channel - - ``'G'`` - Green channel - - ``'B'`` - Blue channel - - ``'A'`` - Alpha channel (``0`` is transparent) - - ``'O'`` - Alpha channel (``0`` is opaque) - - ``'C'`` - Cyan channel - - ``'Y'`` - Yellow channel - - ``'M'`` - Magenta channel - - ``'K'`` - Black channel - - ``'I'`` - Intensity channel (only for grayscale) - - ``'P'`` - Padding - - See :const:`STORAGE_TYPES` for a list of valid - ``storage`` options. This tells ImageMagick - what type of data it should calculate & write to. - For example; a storage type of ``'char'`` will write - a 8-bit value between 0 ~ 255, a storage type - of ``'short'`` will write a 16-bit value between - 0 ~ 65535, and a ``'integer'`` will write a - 32-bit value between 0 ~ 4294967295. - - .. note:: - - By default, the entire image will be exported - as ``'char'`` storage with each pixel mapping - Red, Green, Blue, & Alpha channels. - - - :param x: horizontal starting coordinate of raster. - :type x: :class:`numbers.Integral` - :param y: vertical starting coordinate of raster. - :type y: :class:`numbers.Integral` - :param width: horizontal length of raster. - :type width: :class:`numbers.Integral` - :param height: vertical length of raster. - :type height: :class:`numbers.Integral` - :param channel_map: a string listing the channel data - format for each pixel. - :type channel_map: :class:`basestring` - :param storage: what data type each value should - be calculated as. - :type storage: :class:`basestring` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.6.11 - Update storage type size for `"long"` & `"quantum"` values. - """ - _w, _h = self.size - if width is None: - width = _w - if height is None: - height = _h - assertions.assert_integer(x=x, y=y, width=width, height=height) - assertions.string_in_list(STORAGE_TYPES, 'wand.image.STORAGE_TYPES', - storage=storage) - assertions.assert_string(channel_map=channel_map) - channel_map = channel_map.upper() - valid_channels = 'RGBAOCYMKIP' - for channel in channel_map: - if channel not in valid_channels: - raise ValueError('Unknown channel label: ' + - repr(channel)) - if not isinstance(data, abc.Sequence): - raise TypeError('data must list of values, not' + - repr(data)) - # Ensure enough data was given. - expected_len = width * height * len(channel_map) - given_len = len(data) - if expected_len != given_len: - msg = 'data length should be {0}, not {1}.'.format( - expected_len, - given_len - ) - raise ValueError(msg) - c_storage_types = [ - None, # undefined - ctypes.c_ubyte, # char - ctypes.c_double, # double - ctypes.c_float, # float - ctypes.c_uint, # integer - ctypes.c_uint64, # long - library.PixelGetRedQuantum.restype, # quantum - ctypes.c_ushort # short - ] - s_index = STORAGE_TYPES.index(storage) - c_type = c_storage_types[s_index] - c_buffer = (len(data) * c_type)(*data) - r = library.MagickImportImagePixels(self.wand, - x, y, width, height, - binary(channel_map), - s_index, - ctypes.byref(c_buffer)) - return r - - @trap_exception - def inverse_fourier_transform(self, phase, magnitude=True): - """Applies the inverse of a discrete Fourier transform. The image stack - is replaced with the results. Either a pair of magnitude & phase - images, or real & imaginary (HDRI). - - .. code:: - - from wand.image import Image - - with Image(filename='magnitude.png') as img: - with Image(filename='phase.png') as phase: - img.inverse_fourier_transform(phase) - img.save(filename='output.png') - - .. seealso:: :meth:`forward_fourier_transform` & :meth:`complex` - - .. note:: - - ImageMagick must have HDRI support to compute real & imaginary - components (i.e. ``magnitude=False``). - - :param phase: Second part (image) of the transform. Either the phase, - or the imaginary part. - :type phase: :class:`BaseImage` - :param magnitude: If ``True``, accept magnitude & phase input, else - real & imaginary. Default ``True`` - :type magnitude: :class:`bool` - - .. versionadded:: 0.5.5 - """ - if not isinstance(phase, BaseImage): - raise TypeError('phase must be an image, not ' + repr(phase)) - assertions.assert_bool(magnitude=magnitude) - return library.MagickInverseFourierTransformImage(self.wand, - phase.wand, - magnitude) - - def iterator_first(self): - """Sets the internal image-stack iterator to the first image. - Useful for prepending an image at the start of the stack. - - .. versionadded:: 0.6.2 - """ - library.MagickSetFirstIterator(self.wand) - - def iterator_get(self): - """Returns the position of the internal image-stack index. - - :rtype: :class:`int` - - .. versionadded:: 0.6.2 - """ - return library.MagickGetIteratorIndex(self.wand) - - def iterator_last(self): - """Sets the internal image-stack iterator to the last image. - Useful for appending an image to the end of the stack. - - .. versionadded:: 0.6.2 - """ - library.MagickSetLastIterator(self.wand) - - def iterator_length(self): - """Get the count of images in the image-stack. - - :rtype: :class:`int` - - .. versionadded:: 0.6.2 - """ - return library.MagickGetNumberImages(self.wand) - - def iterator_next(self): - """Steps the image-stack index forward by one - - :rtype: :class:`bool` - - .. versionadded:: 0.6.2 - """ - has_next = library.MagickHasNextImage(self.wand) - if has_next: - idx = library.MagickGetIteratorIndex(self.wand) - has_next = library.MagickSetIteratorIndex(self.wand, idx + 1) - return has_next - - def iterator_previous(self): - """Steps the image-stack index back by one. - - :rtype: :class:`bool` - - .. versionadded:: 0.6.2 - """ - has_prev = library.MagickHasPreviousImage(self.wand) - if has_prev: - idx = library.MagickGetIteratorIndex(self.wand) - has_prev = library.MagickSetIteratorIndex(self.wand, idx - 1) - return has_prev - - def iterator_reset(self): - """Reset internal image-stack iterator. Useful for iterating over the - image-stack without allocating :class:`~wand.sequence.Sequence`. - - .. versionadded:: 0.6.2 - """ - library.MagickResetIterator(self.wand) - - def iterator_set(self, index): - """Sets the index of the internal image-stack. - - :rtype: :class:`bool` - - .. versionadded:: 0.6.2 - """ - assertions.assert_integer(index=index) - return library.MagickSetIteratorIndex(self.wand, index) - - @manipulative - @trap_exception - def kmeans(self, number_colors=None, max_iterations=100, tolerance=0.01): - """Reduces the number of colors in an image by appling the K-means - clustering algorithm. - - .. note:: - - Requires ImageMagick-7.0.10-37, or later. - - :param number_colors: the target number of colors to use as seeds. - :type number_colors: :class:`numbers.Integral` - :param max_iterations: maximum number of iterations needed until - convergence. Default ``100``. - :type max_iterations: :class:`numbers.Integral` - :param tolerance: maximum tolerance between distrotion iterations. - Default ``0.01`` - :type tolerance: :class:`numbers.Real` - - .. versionadded:: 0.6.4 - """ - if MAGICK_VERSION_NUMBER < 0x70A or library.MagickKmeansImage is None: - msg = "Kmeans requires ImageMagick-7.0.10-37 or later." - raise WandLibraryVersionError(msg) - assertions.assert_unsigned_integer(number_colors=number_colors, - max_iterations=max_iterations) - assertions.assert_real(tolerance=tolerance) - return library.MagickKmeansImage(self.wand, number_colors, - max_iterations, tolerance) - - def kurtosis_channel(self, channel='default_channels'): - """Calculates the kurtosis and skewness of the image. - - .. code:: python - - from wand.image import Image - - with Image(filename='input.jpg') as img: - kurtosis, skewness = img.kurtosis_channel() - - :param channel: Select which color channel to evaluate. See - :const:`CHANNELS`. Default ``'default_channels'``. - :type channel: :class:`basestring` - :returns: Tuple of :attr:`kurtosis` & :attr:`skewness` - values. - :rtype: :class:`tuple` - - .. versionadded:: 0.5.3 - """ - ch_channel = self._channel_to_mask(channel) - k = ctypes.c_double(0.0) - s = ctypes.c_double(0.0) - if MAGICK_VERSION_NUMBER < 0x700: - library.MagickGetImageChannelKurtosis(self.wand, ch_channel, - ctypes.byref(k), - ctypes.byref(s)) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_channel) - library.MagickGetImageKurtosis(self.wand, - ctypes.byref(k), - ctypes.byref(s)) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return k.value, s.value - - @manipulative - @trap_exception - def kuwahara(self, radius=1.0, sigma=None): - """Edge preserving noise reduction filter. - - https://en.wikipedia.org/wiki/Kuwahara_filter - - If ``sigma`` is not given, the value will be calculated as: - - sigma = radius - 0.5 - - To match original algorithm's behavior, increase ``radius`` value by - one: - - myImage.kuwahara(myRadius + 1, mySigma) - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :see: Example of :ref:`kuwahara`. - - :param radius: Size of the filter aperture. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of Gaussian filter. - :type sigma: :class:`numbers.Real` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickKuwaharaImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - if sigma is None: - sigma = radius - 0.5 - assertions.assert_real(radius=radius, sigma=sigma) - return library.MagickKuwaharaImage(self.wand, radius, sigma) - - @manipulative - def label(self, text, left=None, top=None, font=None, gravity=None, - background_color='transparent'): - """Writes a label ``text`` into the position on top of the existing - canvas. This method doesn't autofit text like :meth:`caption`. Use - ``left`` & ``top``, or ``gravity``, to position the text. - - :param text: text to write. - :type text: :class:`basestring` - :param left: x offset in pixels. - :type left: :class:`numbers.Integral` - :param top: y offset in pixels. - :type top: :class:`numbers.Integral` - :param font: font to use. default is :attr:`font` of the image. - :type font: :class:`wand.font.Font` - :param gravity: text placement gravity. - :type gravity: :class:`basestring` - - .. versionadded:: 0.6.8 - """ - if font is not None and not isinstance(font, Font): - raise TypeError('font must be a wand.font.Font, not ' + repr(font)) - if gravity is not None: - assertions.string_in_list(GRAVITY_TYPES, - 'wand.image.GRAVITY_TYPES', - gravity=gravity) - if font is None: - try: - font = self.font - if font is None: - raise TypeError() - except TypeError: - raise TypeError('font must be specified or existing in image') - with Image() as textboard: - textboard.font = font - textboard.background_color = background_color - textboard.read(filename=b'label:' + text.encode('utf-8')) - self.composite(textboard, left=left, top=top, gravity=gravity) - - @trap_exception - def level(self, black=0.0, white=None, gamma=1.0, channel=None): - """Adjusts the levels of an image by scaling the colors falling - between specified black and white points to the full available - quantum range. - - If only ``black`` is given, ``white`` will be adjusted inward. - - :see: Example of :ref:`level`. - - :param black: Black point, as a percentage of the system's quantum - range. Defaults to 0. - :type black: :class:`numbers.Real` - :param white: White point, as a percentage of the system's quantum - range. Defaults to 1.0. - :type white: :class:`numbers.Real` - :param gamma: Optional gamma adjustment. Values > 1.0 lighten the - image's midtones while values < 1.0 darken them. - :type gamma: :class:`numbers.Real` - :param channel: The channel type. Available values can be found - in the :const:`CHANNELS` mapping. If ``None``, - normalize all channels. - :type channel: :const:`CHANNELS` - - .. note:: - Images may not be affected if the ``white`` value is equal to or - less than the ``black`` value. - - .. versionadded:: 0.4.1 - - """ - assertions.assert_real(black=black) - # If white is not given, mimic CLI behavior by reducing top point - if white is None: - white = 1.0 - black - assertions.assert_real(white=white, gamma=gamma) - - bp = float(self.quantum_range * black) - wp = float(self.quantum_range * white) - if MAGICK_HDRI: # pragma: no cover - bp -= 0.5 # TODO: Document why HDRI requires 0.5 adjustments. - wp -= 0.5 - if channel is None: - r = library.MagickLevelImage(self.wand, bp, gamma, wp) - else: - ch_const = self._channel_to_mask(channel) - if library.MagickLevelImageChannel: - r = library.MagickLevelImageChannel(self.wand, - ch_const, - bp, - gamma, - wp) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickLevelImage(self.wand, bp, gamma, wp) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def level_colors(self, black_color, white_color, channel=None): - """Maps given colors to "black" & "white" values. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-54, or - greater. - - :param black_color: linearly map given color as "black" point. - :type black_color: :class:`Color` - :param white_color: linearly map given color as "white" point. - :type white_color: :class:`Color` - :param channel: target a specific color-channel to levelize. - :type channel: :class:`basestring` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.6 - """ - if library.MagickLevelImageColors is None: - msg = 'Method requires ImageMagick version 7.0.8-54 or greater.' - raise WandLibraryVersionError(msg) - if isinstance(black_color, string_type): - black_color = Color(black_color) - if isinstance(white_color, string_type): - white_color = Color(white_color) - assertions.assert_color(black_color=black_color, - white_color=white_color) - channel_mask = None - if channel is not None: - ch_const = self._channel_to_mask(channel) - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - with black_color: - with white_color: - r = library.MagickLevelImageColors(self.wand, - black_color.resource, - white_color.resource, - False) - if channel is not None: - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def levelize(self, black=0.0, white=None, gamma=1.0, channel=None): - """Reverse of :meth:`level()`, this method compresses the range of - colors between ``black`` & ``white`` values. - - If only ``black`` is given, ``white`` will be adjusted inward. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param black: Black point, as a percentage of the system's quantum - range. Defaults to 0. - :type black: :class:`numbers.Real` - :param white: White point, as a percentage of the system's quantum - range. Defaults to 1.0. - :type white: :class:`numbers.Real` - :param gamma: Optional gamma adjustment. Values > 1.0 lighten the - image's midtones while values < 1.0 darken them. - :type gamma: :class:`numbers.Real` - :param channel: The channel type. Available values can be found - in the :const:`CHANNELS` mapping. If ``None``, - normalize all channels. - :type channel: :const:`CHANNELS` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickLevelizeImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - if white is None: - white = float(self.quantum_range) - assertions.assert_real(black=black, white=white, gamma=gamma) - if 0 < black <= 1.0: - black *= self.quantum_range - if 0 < white <= 1.0: - white *= self.quantum_range - if channel is None: - r = library.MagickLevelizeImage(self.wand, black, gamma, white) - else: - ch_const = self._channel_to_mask(channel) - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickLevelizeImage(self.wand, black, gamma, white) - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def levelize_colors(self, black_color, white_color, channel=None): - """Reverse of :meth:`level_colors()`, and creates a de-contrasting - gradient of given colors. This works best with grayscale images. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-54, or - greater. - - :param black_color: tint map given color as "black" point. - :type black_color: :class:`Color` - :param white_color: tint map given color as "white" point. - :type white_color: :class:`Color` - :param channel: target a specific color-channel to levelize. - :type channel: :class:`basestring` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.6 - """ - if library.MagickLevelImageColors is None: - msg = 'Method requires ImageMagick version 7.0.8-54 or greater.' - raise WandLibraryVersionError(msg) - if isinstance(black_color, string_type): - black_color = Color(black_color) - if isinstance(white_color, string_type): - white_color = Color(white_color) - assertions.assert_color(black_color=black_color, - white_color=white_color) - channel_mask = None - ch_const = None - if channel is not None: - ch_const = self._channel_to_mask(channel) - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - with black_color: - with white_color: - r = library.MagickLevelImageColors(self.wand, - black_color.resource, - white_color.resource, - True) - if channel is not None: - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def linear_stretch(self, black_point=0.0, white_point=1.0): - """Enhance saturation intensity of an image. - - :param black_point: Black point between 0.0 and 1.0. Default 0.0 - :type black_point: :class:`numbers.Real` - :param white_point: White point between 0.0 and 1.0. Default 1.0 - :type white_point: :class:`numbers.Real` - - .. versionadded:: 0.4.1 - """ - assertions.assert_real(black_point=black_point, - white_point=white_point) - linear_range = float(self.width * self.height) - return library.MagickLinearStretchImage(self.wand, - linear_range * black_point, - linear_range * white_point) - - @manipulative - def liquid_rescale(self, width, height, delta_x=0, rigidity=0): - """Rescales the image with `seam carving`_, also known as - image retargeting, content-aware resizing, or liquid rescaling. - - :param width: the width in the scaled image - :type width: :class:`numbers.Integral` - :param height: the height in the scaled image - :type height: :class:`numbers.Integral` - :param delta_x: maximum seam transversal step. - 0 means straight seams. default is 0 - :type delta_x: :class:`numbers.Real` - :param rigidity: introduce a bias for non-straight seams. - default is 0 - :type rigidity: :class:`numbers.Real` - :raises wand.exceptions.MissingDelegateError: - when ImageMagick isn't configured ``--with-lqr`` option. - - .. note:: - - This feature requires ImageMagick to be configured - ``--with-lqr`` option. Or it will raise - :exc:`~wand.exceptions.MissingDelegateError`: - - .. seealso:: - - `Seam carving`_ --- Wikipedia - The article which explains what seam carving is - on Wikipedia. - - .. _Seam carving: http://en.wikipedia.org/wiki/Seam_carving - - """ - assertions.assert_integer(width=width, height=height) - assertions.assert_real(delta_x=delta_x, rigidity=rigidity) - library.MagickLiquidRescaleImage(self.wand, width, height, - delta_x, rigidity) - try: - self.raise_exception() - except MissingDelegateError as e: # pragma: no cover - raise MissingDelegateError( - str(e) + '\n\nImageMagick in the system is likely to be ' - 'impossible to load liblqr. You might not install liblqr, ' - 'or ImageMagick may not compiled with liblqr.' - ) - - @manipulative - @trap_exception - def local_contrast(self, radius=10, strength=12.5): - """Increase light-dark transitions within image. - - .. warning:: - - This class method is only available with ImageMagick 6.9.3, or - greater. - - :param radius: The size of the Gaussian operator. Default value is - ``10.0``. - :type radius: :class:`numbers.Real` - :param strength: Percentage of blur mask to apply. Values can be - between ``0.0`` and ``100`` with a default of - ``12.5``. - :type strength: :class:`numbers.Real` - - .. versionadded:: 0.5.7 - """ - if library.MagickLocalContrastImage is None: # pragma: no cover - msg = 'Method requires ImageMagick version 6.9.3 or greater.' - raise WandLibraryVersionError(msg) - assertions.assert_real(radius=radius, strength=strength) - return library.MagickLocalContrastImage(self.wand, radius, strength) - - @manipulative - @trap_exception - def magnify(self): - """Quickly double an image in size. This is a convenience method. - Use :meth:`resize()`, :meth:`resample()`, or :meth:`sample()` for - more control. - - .. versionadded:: 0.5.5 - """ - return library.MagickMagnifyImage(self.wand) - - def mean_channel(self, channel='default_channels'): - """Calculates the mean and standard deviation of the image. - - .. code:: python - - from wand.image import Image - - with Image(filename='input.jpg') as img: - mean, stddev = img.mean_channel() - - :param channel: Select which color channel to evaluate. See - :const:`CHANNELS`. Default ``'default_channels'``. - :type channel: :class:`basestring` - :returns: Tuple of :attr:`mean` & :attr:`standard_deviation` - values. The ``mean`` value will be between 0.0 & - :attr:`quantum_range` - :rtype: :class:`tuple` - - .. versionadded:: 0.5.3 - """ - ch_channel = self._channel_to_mask(channel) - m = ctypes.c_double(0.0) - s = ctypes.c_double(0.0) - if MAGICK_VERSION_NUMBER < 0x700: - library.MagickGetImageChannelMean(self.wand, ch_channel, - ctypes.byref(m), - ctypes.byref(s)) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_channel) - library.MagickGetImageMean(self.wand, - ctypes.byref(m), - ctypes.byref(s)) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return m.value, s.value - - @manipulative - @trap_exception - def mean_shift(self, width, height, color_distance=0.1): - """Recalculates pixel value by comparing neighboring pixels within a - color distance, and replacing with a mean value. Works best with - Gray, YCbCr, YIQ, or YUV colorspaces. - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param width: Size of the neighborhood window in pixels. - :type width: :class:`numbers.Integral` - :param height: Size of the neighborhood window in pixels. - :type height: :class:`numbers.Integral` - :param color_distance: Include pixel values within this color distance. - :type color_distance: :class:`numbers.Real` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickMeanShiftImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - assertions.assert_counting_number(width=width, height=height) - assertions.assert_real(color_distance=color_distance) - if 0 < color_distance <= 1.0: - color_distance *= self.quantum_range - return library.MagickMeanShiftImage(self.wand, width, height, - color_distance) - - @manipulative - @trap_exception - def merge_layers(self, method): - """Composes all the image layers from the current given image onward - to produce a single image of the merged layers. - - The initial canvas's size depends on the given ImageLayerMethod, and is - initialized using the first images background color. The images - are then composited onto that image in sequence using the given - composition that has been assigned to each individual image. - The method must be set with a value from :const:`IMAGE_LAYER_METHOD` - that is acceptable to this operation. (See ImageMagick documentation - for more details.) - - :param method: the method of selecting the size of the initial canvas. - :type method: :class:`basestring` - - .. versionadded:: 0.4.3 - - """ - assertions.assert_string(method=method) - if method not in ('merge', 'flatten', 'mosaic', 'trimbounds'): - raise ValueError('method can only be \'merge\', \'flatten\', ' - '\'mosaic\', or \'trimbounds\'') - m = IMAGE_LAYER_METHOD.index(method) - r = library.MagickMergeImageLayers(self.wand, m) - if r: - self.wand = r - self.reset_sequence() - return bool(r) - - def minimum_bounding_box(self, orientation=None): - """Find the minmum bounding box within the image. Use - properties :attr:`fuzz` & :attr:`background_color` to influence - bounding box thresholds. - - .. code:: - - from wand.image import Image - from wand.drawing import Drawing - - with Image(filename='kdf_black.png') as img: - img.fuzz = img.quantum_range * 0.1 - img.background_color = 'black' - mbr = img.minimum_bounding_box() - with Drawing() as ctx: - ctx.fill_color = 'transparent' - ctx.stroke_color = 'red' - ctx.polygon(points=mbr['points']) - ctx.fill_color = 'red' - ctx.stroke_color = 'transparent' - ctx.text(1, 10, '{0:.4g}\u00B0'.format(mbr['angle'])) - ctx(img) - img.save(filename='kdf_black_mbr.png') - - .. image:: ../_images/wand/image/kdf_black.png - .. image:: ../_images/wand/image/kdf_black_mbr.png - - .. note:: - - Requires ImageMagick-7.0.10 or later. - - :param orientation: sets the image orientation. Values can be - ``'landscape'``, or ``'portrait'``. - :type orientation: :class:`basestring` - :returns: a directory of MBR properties & corner points. - :rtype: :class:`dict` { "points": :class:`list` [ :class:`tuple` ( - :class:`float`, :class:`float` ) ], "area": :class:`float`, - "width": :class:`float`, "height": :class:`float`, - "angle": :class:`float`, "unrotate": :class:`float` } - - .. versionadded:: 0.6.4 - """ - r = {} - if MAGICK_VERSION_NUMBER < 0x70A: - msg = 'ImageMagick-7.0.10 is required to use convex_hull().' - raise WandLibraryVersionError(msg) - with self.clone() as tmp: - if orientation is not None: - if orientation not in ('landscape', 'portrait'): - msg = 'orientation can only be landscape, or portrait, not' - msg += ' ' + repr(orientation) - raise ValueError(msg) - key = b'minimum-bounding-box:orientation' - val = to_bytes(orientation) - library.MagickSetImageArtifact(tmp.wand, key, val) - mbr_str = b'%[minimum-bounding-box]' - mbr_str += b'|%[minimum-bounding-box:area]' - mbr_str += b'|%[minimum-bounding-box:width]' - mbr_str += b'|%[minimum-bounding-box:height]' - mbr_str += b'|%[minimum-bounding-box:angle]' - mbr_str += b'|%[minimum-bounding-box:unrotate]' - library.MagickSetOption(tmp.wand, b'format', mbr_str) - library.MagickSetImageFormat(tmp.wand, b'INFO') - length = ctypes.c_size_t() - blob_p = library.MagickGetImageBlob(tmp.wand, - ctypes.byref(length)) - if blob_p: - blob = ctypes.string_at(blob_p, length.value) - blob_p = library.MagickRelinquishMemory(blob_p) - parts = blob.decode('ascii', 'ignore').split('|') - pts = parts[0].strip().split(' ') - r = [tuple(map(lambda x: float(x), p.split(','))) for p in pts] - attr = list(map(lambda x: float(x.strip()), parts[1:])) - keys = ['area', 'width', 'height', 'angle', 'unrotate'] - r = dict(zip(keys, attr), points=r) - else: - self.raise_exception() - return r - - @manipulative - def mode(self, width, height=None): - """Replace each pixel with the mathematical mode of the neighboring - colors. This is an alias of the :meth:`statistic` method. - - :param width: Number of neighboring pixels to include in mode. - :type width: :class:`numbers.Integral` - :param height: Optional height of neighboring pixels, defaults to the - same value as ``width``. - :type height: :class:`numbers.Integral` - - .. versionadded:: 0.5.4 - """ - if height is None: - height = width - self.statistic('mode', width, height) - - @manipulative - @trap_exception - def modulate(self, brightness=100.0, saturation=100.0, hue=100.0): - """Changes the brightness, saturation and hue of an image. - We modulate the image with the given ``brightness``, ``saturation`` - and ``hue``. - - :param brightness: percentage of brightness - :type brightness: :class:`numbers.Real` - :param saturation: percentage of saturation - :type saturation: :class:`numbers.Real` - :param hue: percentage of hue rotation - :type hue: :class:`numbers.Real` - :raises ValueError: when one or more arguments are invalid - - .. versionadded:: 0.3.4 - - """ - assertions.assert_real(brightness=brightness, saturation=saturation, - hue=hue) - return library.MagickModulateImage( - self.wand, - brightness, - saturation, - hue - ) - - @manipulative - @trap_exception - def morphology(self, method=None, kernel=None, iterations=1, channel=None): - """Manipulate pixels based on the shape of neighboring pixels. - - The ``method`` determines what type of effect to apply to matching - ``kernel`` shapes. Common methods can be add/remove, - or lighten/darken pixel values. - - The ``kernel`` describes the shape of the matching neighbors. Common - shapes are provided as "built-in" kernels. See - :const`KERNEL_INFO_TYPES` for examples. The format for built-in kernels - is: - - .. sourcecode:: text - - label:geometry - - Where `label` is the kernel name defined in :const:`KERNEL_INFO_TYPES`, - and `:geometry` is an optional geometry size. For example:: - - with Image(filename='rose:') as img: - img.morphology(method='dilate', kernel='octagon:3x3') - # or simply - img.morphology(method='edgein', kernel='octagon') - - Custom kernels can be applied by following a similar format: - - .. sourcecode:: text - - geometry:args - - Where `geometry` is the size of the custom kernel, and `args` - list a comma separated list of values. For example:: - - custom_kernel='5x3:nan,1,1,1,nan 1,1,1,1,1 nan,1,1,1,nan' - with Image(filename='rose:') as img: - img.morphology(method='dilate', kernel=custom_kernel) - - :param method: effect function to apply. See - :const:`MORPHOLOGY_METHODS` for a list of - methods. - :type method: :class:`basestring` - :param kernel: shape to evaluate surrounding pixels. See - :const:`KERNEL_INFO_TYPES` for a list of - built-in shapes. - :type kernel: :class:`basestring` - :param iterations: Number of times a morphology method should be - applied to the image. Default ``1``. Use ``-1`` for - unlimited iterations until the image is unchanged - by the method operator. - :type iterations: :class:`numbers.Integral` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: `basestring` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - assertions.assert_string(method=method, kernel=kernel) - assertions.assert_integer(iterations=iterations) - buitin = None - geometry = '' - parts = kernel.split(':') - if parts[0] in KERNEL_INFO_TYPES: - buitin = parts[0] - if len(parts) == 2: - geometry = parts[1] - exception_info = libmagick.AcquireExceptionInfo() - if buitin: - kernel_idx = KERNEL_INFO_TYPES.index(buitin) - geometry_info = GeometryInfo() - flags = libmagick.ParseGeometry(binary(geometry), - ctypes.byref(geometry_info)) - if buitin in ('unity',): - if (flags & geometry_info.RhoValue) == 0: - geometry_info.rho = 1.0 - elif buitin in ('square', 'diamond', 'octagon', 'disk', - 'plus', 'cross'): - if (flags & geometry_info.SigmaValue) == 0: - geometry_info.sigma = 1.0 - elif buitin in ('ring',): - if (flags & geometry_info.XiValue) == 0: - geometry_info.xi = 1.0 - elif buitin in ('rectangle',): - if (flags & geometry_info.RhoValue) == 0: - geometry_info.rho = geometry_info.sigma - if geometry_info.rho < 1.0: - geometry_info.rho = 3.0 - if geometry_info.sigma < 1.0: - geometry_info.sigma = geometry_info.rho - if (flags & geometry_info.XiValue) == 0: - geometry_info.xi = (geometry_info.rho - 1.0) / 2.0 - if (flags & geometry_info.PsiValue) == 0: - geometry_info.psi = (geometry_info.sigma - 1.0) / 2.0 - elif buitin in ('chebyshev', 'manhattan', 'octagonal', - 'euclidean'): - if (flags & geometry_info.SigmaValue) == 0: - geometry_info.sigma = 100.0 - elif (flags & geometry_info.AspectValue) != 0: - geometry_info.sigma = (float(self.quantum_range) / - (geometry_info.sigma + 1.0)) - elif (flags & geometry_info.PercentValue) != 0: - geometry_info.sigma *= float(self.quantum_range) / 100.0 - if MAGICK_VERSION_NUMBER < 0x700: - kernel_info = libmagick.AcquireKernelBuiltIn( - kernel_idx, - ctypes.byref(geometry_info) - ) - else: # pragma: no cover - kernel_info = libmagick.AcquireKernelBuiltIn( - kernel_idx, - ctypes.byref(geometry_info), - exception_info - ) - elif kernel: - if MAGICK_VERSION_NUMBER < 0x700: - kernel_info = libmagick.AcquireKernelInfo( - binary(kernel) - ) - else: # pragma: no cover - kernel_info = libmagick.AcquireKernelInfo( - binary(kernel), - exception_info - ) - r = None - exception_info = libmagick.DestroyExceptionInfo(exception_info) - if kernel_info: - method_idx = MORPHOLOGY_METHODS.index(method) - if channel is None: - r = library.MagickMorphologyImage(self.wand, method_idx, - iterations, kernel_info) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickMorphologyImageChannel(self.wand, - channel_ch, - method_idx, - iterations, - kernel_info) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickMorphologyImage(self.wand, method_idx, - iterations, kernel_info) - library.MagickSetImageChannelMask(self.wand, mask) - kernel_info = libmagick.DestroyKernelInfo(kernel_info) - else: - raise ValueError('Unable to parse kernel info for ' + - repr(kernel)) - return r - - @manipulative - @trap_exception - def motion_blur(self, radius=0.0, sigma=0.0, angle=0.0, channel=None): - """Apply a Gaussian blur along an ``angle`` direction. This - simulates motion movement. - - :see: Example of :ref:`motion_blur`. - - :param radius: Aperture size of the Gaussian operator. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of the Gaussian operator. - :type sigma: :class:`numbers.Real` - :param angle: Apply the effect along this angle. - :type angle: :class:`numbers.Real` - - .. versionadded:: 0.5.4 - """ - assertions.assert_real(radius=radius, sigma=sigma, angle=angle) - if channel is None: - r = library.MagickMotionBlurImage(self.wand, radius, sigma, angle) - else: - ch_const = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickMotionBlurImageChannel(self.wand, - ch_const, - radius, - sigma, - angle) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickMotionBlurImage(self.wand, radius, sigma, - angle) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def negate(self, grayscale=False, channel=None): - """Negate the colors in the reference image. - - :param grayscale: if set, only negate grayscale pixels in the image. - :type grayscale: :class:`bool` - :param channel: the channel type. available values can be found - in the :const:`CHANNELS` mapping. If ``None``, - negate all channels. - :type channel: :class:`basestring` - - .. versionadded:: 0.3.8 - - """ - if channel is None: - r = library.MagickNegateImage(self.wand, grayscale) - else: - ch_const = self._channel_to_mask(channel) - if library.MagickNegateImageChannel: - r = library.MagickNegateImageChannel(self.wand, ch_const, - grayscale) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_const) - r = library.MagickNegateImage(self.wand, grayscale) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def noise(self, noise_type='uniform', attenuate=1.0, channel=None): - """Adds noise to image. - - :see: Example of :ref:`noise`. - - :param noise_type: type of noise to apply. See :const:`NOISE_TYPES`. - :type noise_type: :class:`basestring` - :param attenuate: rate of distribution. Only available in - ImageMagick-7. Default is ``1.0``. - :type attenuate: :class:`numbers.Real` - :param channel: Optionally target a color channel to apply noise to. - See :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument. - """ - assertions.string_in_list(NOISE_TYPES, 'wand.image.NOISE_TYPES', - noise_type=noise_type) - assertions.assert_real(attenuate=attenuate) - noise_type_idx = NOISE_TYPES.index(noise_type) - if MAGICK_VERSION_NUMBER < 0x700: - if channel is None: - r = library.MagickAddNoiseImage(self.wand, noise_type_idx) - else: - channel_ch = self._channel_to_mask(channel) - r = library.MagickAddNoiseImageChannel(self.wand, - channel_ch, - noise_type_idx) - else: # pragma: no cover - if channel is None: - r = library.MagickAddNoiseImage(self.wand, noise_type_idx, - attenuate) - else: - channel_ch = self._channel_to_mask(channel) - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickAddNoiseImage(self.wand, noise_type_idx, - attenuate) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def normalize(self, channel=None): - """Normalize color channels. - - :param channel: the channel type. available values can be found - in the :const:`CHANNELS` mapping. If ``None``, - normalize all channels. - :type channel: :class:`basestring` - - """ - if channel is None: - r = library.MagickNormalizeImage(self.wand) - else: - ch_const = self._channel_to_mask(channel) - if library.MagickNormalizeImageChannel: - r = library.MagickNormalizeImageChannel(self.wand, ch_const) - else: # pragma: no cover - with Image(image=self) as mask: - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(mask.wand, - ch_const) - r = library.MagickNormalizeImage(mask.wand) - # Restore original state of channels. - library.MagickSetImageChannelMask(mask.wand, - channel_mask) - # Copy adjusted mask over original value. - copy_mask = COMPOSITE_OPERATORS.index('copy_' + channel) - library.MagickCompositeImage(self.wand, - mask.wand, - copy_mask, - False, - 0, - 0) - return r - - @manipulative - @trap_exception - def oil_paint(self, radius=0.0, sigma=0.0): - """Simulates an oil painting by replace each pixel with most frequent - surrounding color. - - :param radius: The size of the surrounding neighbors. - :type radius: :class:`numbers.Real` - :param sigma: The standard deviation used by the Gaussian operator. - This is only available with ImageMagick-7. - :type sigma: :class:`numbers.Real` - - .. versionadded:: 0.5.4 - """ - assertions.assert_real(radius=radius, sigma=sigma) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickOilPaintImage(self.wand, radius) - else: # pragma: no cover - r = library.MagickOilPaintImage(self.wand, radius, sigma) - return r - - @manipulative - @trap_exception - def opaque_paint(self, target=None, fill=None, fuzz=0.0, invert=False, - channel=None): - """Replace any color that matches ``target`` with ``fill``. Use - ``fuzz`` to control the threshold of the target match. - The ``invert`` will replace all colors *but* the pixels matching - the ``target`` color. - - :param target: The color to match. - :type target: :class:`wand.color.Color` - :param fill: The color to paint with. - :type fill: :class:`wand.color.Color` - :param fuzz: Normalized real number between `0.0` and - :attr:`quantum_range`. Default is `0.0`. - :type fuzz: class:`numbers.Real` - :param invert: Replace all colors that do not match target. - Default is ``False``. - :type invert: :class:`bool` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.5.4 - - .. versionchanged:: 0.5.5 - Added ``channel`` paramater. - """ - if isinstance(target, string_type): - target = Color(target) - if isinstance(fill, string_type): - fill = Color(fill) - assertions.assert_color(target=target, fill=fill) - assertions.assert_real(fuzz=fuzz) - assertions.assert_bool(invert=invert) - with target: - with fill: - if channel is None: - r = library.MagickOpaquePaintImage(self.wand, - target.resource, - fill.resource, - fuzz, - invert) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickOpaquePaintImageChannel( - self.wand, channel_ch, target.resource, - fill.resource, fuzz, invert - ) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickOpaquePaintImage(self.wand, - target.resource, - fill.resource, - fuzz, - invert) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def optimize_layers(self): - """Attempts to crop each frame to the smallest image without altering - the animation. For best results, call - :meth:`Image.coalesce() ` before - manipulating any frames. For timing accuracy, any - :attr:`SingleImage.delay ` overwrites - must be applied after optimizing layers. - - .. note:: - - This will only affect ``GIF`` image formates. - - .. versionadded:: 0.5.0 - """ - r = library.MagickOptimizeImageLayers(self.wand) - if r: - self.wand = r - self.reset_sequence() - return bool(r) - - @manipulative - @trap_exception - def optimize_transparency(self): - """Iterates over frames, and sets transparent values for each - pixel unchanged by previous frame. - - .. note:: - - This will only affect ``GIF`` image formates. - - .. versionadded:: 0.5.0 - """ - if library.MagickOptimizeImageTransparency: - return library.MagickOptimizeImageTransparency(self.wand) - else: # pragma: no cover - raise AttributeError('`MagickOptimizeImageTransparency\' not ' - 'available on current version of MagickWand ' - 'library.') - - @manipulative - @trap_exception - def ordered_dither(self, threshold_map='threshold', channel=None): - """Executes a ordered-based dither operations based on predetermined - threshold maps. - - +-----------+-------+-----------------------------+ - | Map | Alias | Description | - +===========+=======+=============================+ - | threshold | 1x1 | Threshold 1x1 (non-dither) | - +-----------+-------+-----------------------------+ - | checks | 2x1 | Checkerboard 2x1 (dither) | - +-----------+-------+-----------------------------+ - | o2x2 | 2x2 | Ordered 2x2 (dispersed) | - +-----------+-------+-----------------------------+ - | o3x3 | 3x3 | Ordered 3x3 (dispersed) | - +-----------+-------+-----------------------------+ - | o4x4 | 4x4 | Ordered 4x4 (dispersed) | - +-----------+-------+-----------------------------+ - | o8x8 | 8x8 | Ordered 8x8 (dispersed) | - +-----------+-------+-----------------------------+ - | h4x4a | 4x1 | Halftone 4x4 (angled) | - +-----------+-------+-----------------------------+ - | h6x6a | 6x1 | Halftone 6x6 (angled) | - +-----------+-------+-----------------------------+ - | h8x8a | 8x1 | Halftone 8x8 (angled) | - +-----------+-------+-----------------------------+ - | h4x4o | | Halftone 4x4 (orthogonal) | - +-----------+-------+-----------------------------+ - | h6x6o | | Halftone 6x6 (orthogonal) | - +-----------+-------+-----------------------------+ - | h8x8o | | Halftone 8x8 (orthogonal) | - +-----------+-------+-----------------------------+ - | h16x16o | | Halftone 16x16 (orthogonal) | - +-----------+-------+-----------------------------+ - | c5x5b | c5x5 | Circles 5x5 (black) | - +-----------+-------+-----------------------------+ - | c5x5w | | Circles 5x5 (white) | - +-----------+-------+-----------------------------+ - | c6x6b | c6x6 | Circles 6x6 (black) | - +-----------+-------+-----------------------------+ - | c6x6w | | Circles 6x6 (white) | - +-----------+-------+-----------------------------+ - | c7x7b | c7x7 | Circles 7x7 (black) | - +-----------+-------+-----------------------------+ - | c7x7w | | Circles 7x7 (white) | - +-----------+-------+-----------------------------+ - - :param threshold_map: Name of threshold dither to use, followed by - optional arguments. - :type threshold_map: :class:`basestring` - :param channel: Optional argument to apply dither to specific color - channel. See :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.7 - """ - assertions.assert_string(threshold_map=threshold_map) - bmap = binary(threshold_map) - if MAGICK_VERSION_NUMBER <= 0x700: - if channel is None: - r = library.MagickOrderedPosterizeImage(self.wand, bmap) - else: - channel_ch = self._channel_to_mask(channel) - r = library.MagickOrderedPosterizeImageChannel(self.wand, - channel_ch, - bmap) - else: # pragma: no cover - if channel is None: - r = library.MagickOrderedDitherImage(self.wand, bmap) - else: - channel_ch = self._channel_to_mask(channel) - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickOrderedDitherImage(self.wand, bmap) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - def parse_meta_geometry(self, geometry): - """Helper method to translate geometry format, and calculate - meta-characters against image dimensions. - - See "Image Geometry" definitions & examples for more info: - https://imagemagick.org/script/command-line-processing.php#geometry - - :param geometry: user string following ImageMagick's geometry format. - :type geometry: :class:`basestring` - :returns: Calculated width, height, offset-x, & offset-y. - :rtype: :class:`tuple` - :raises ValueError: If given geometry can not be parsed. - - .. versionadded:: 0.5.6 - """ - assertions.assert_string(geometry=geometry) - x = ctypes.c_ssize_t(0) - y = ctypes.c_ssize_t(0) - width = ctypes.c_size_t(self.width) - height = ctypes.c_size_t(self.height) - r = libmagick.ParseMetaGeometry(binary(geometry), - ctypes.byref(x), - ctypes.byref(y), - ctypes.byref(width), - ctypes.byref(height)) - if not bool(r): - raise ValueError('Unable to parse geometry') - return (width.value, height.value, x.value, y.value) - - def percent_escape(self, string_format): - """Convenience method that expands ImageMagick's `Percent Escape`_ - characters into image attribute values. - - .. _Percent Escape: https://imagemagick.org/script/escape.php - - .. code:: - - with wand.image import Image - - with Image(filename='tests/assets/sasha.jpg') as img: - print(img.percent_escape('%f %wx%h')) - #=> sasha.jpg 204x247 - - .. note:: - - Not all percent escaped values can be populated as I/O operations - are managed by Python, and not the CLI utility. - - :param string_format: The precent escaped string to be translated. - :type string_format: :class:`basestring` - :returns: String of expanded values. - :rtype: :class:`basestring` - - .. versionadded:: 0.5.6 - """ - local_overwrites = { - '%m': self.format, - '%[magick]': self.format - } - for k, v in local_overwrites.items(): - string_format = string_format.replace(k, v) - self.options['format'] = string_format - return text(self.make_blob('INFO')) - - @manipulative - @trap_exception - def polaroid(self, angle=0.0, caption=None, font=None, method='undefined'): - """Creates a special effect simulating a Polaroid photo. - - :see: Example of :ref:`polaroid`. - - :param angle: applies a shadow effect along this angle. - :type angle: :class:`numbers.Real` - :param caption: Writes a message at the bottom of the photo's border. - :type caption: :class:`basestring` - :param font: Specify font style. - :type font: :class:`wand.font.Font` - :param method: Interpolation method. ImageMagick-7 only. - :type method: :class:`basestring` - - .. versionadded:: 0.5.4 - """ - assertions.assert_real(angle=angle) - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - method=method) - ctx_ptr = library.NewDrawingWand() - if caption: - assertions.assert_string(caption=caption) - caption = binary(caption) - library.MagickSetImageProperty(self.wand, b'Caption', - caption) - if isinstance(font, Font): - if font.path: - library.DrawSetFont(ctx_ptr, binary(font.path)) - if font.size: - library.DrawSetFontSize(ctx_ptr, font.size) - if font.color: - with font.color: - library.DrawSetFillColor(ctx_ptr, font.color.resource) - library.DrawSetTextAntialias(ctx_ptr, font.antialias) - if font.stroke_color: - with font.stroke_color: - library.DrawSetStrokeColor(ctx_ptr, - font.stroke_color.resource) - if font.stroke_width: - library.DrawSetStrokeWidth(ctx_ptr, font.stroke_width) - elif font: - raise TypeError('font must be in instance of ' - 'wand.font.Font, not ' + repr(font)) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickPolaroidImage(self.wand, ctx_ptr, angle) - else: # pragma: no cover - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - r = library.MagickPolaroidImage(self.wand, ctx_ptr, caption, angle, - method_idx) - ctx_ptr = library.DestroyDrawingWand(ctx_ptr) - return r - - @manipulative - @trap_exception - def polynomial(self, arguments): - """Replace image with the sum of all images in a sequence by - calculating the pixel values a coefficient-weight value, and a - polynomial-exponent. - - For example:: - - with Image(filename='rose:') as img: - img.polynomial(arguments=[0.5, 1.0]) - - The output image will be calculated as: - - .. math:: - - output = 0.5 * image ^ {1.0} - - This can work on multiple images in a sequence by calculating across - each frame in the image stack. - - .. code:: - - with Image(filename='2frames.gif') as img: - img.polynomial(arguments=[0.5, 1.0, 0.25, 1.25]) - - Where the results would be calculated as: - - .. math:: - - output = 0.5 * frame1 ^ {1.0} + 0.25 * frame2 ^ {1.25} - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param arguments: A list of real numbers where at least two numbers - (weight & exponent) are need for each image in the - sequence. - :type arguments: :class:`collections.abc.Sequence` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickPolynomialImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - if not isinstance(arguments, abc.Sequence): - raise TypeError('expected sequence of doubles, not ' + - repr(arguments)) - argc = len(arguments) - argv = (ctypes.c_double * argc)(*arguments) - return library.MagickPolynomialImage(self.wand, (argc >> 1), argv) - - @manipulative - @trap_exception - def posterize(self, levels=None, dither='no'): - """Reduce color levels per channel. - - :param levels: Number of levels per channel. - :type levels: :class:`numbers.Integral` - :param dither: Dither method to apply. - See :const:`DITHER_METHODS`. - :type dither: `basestring` - - .. versionadded:: 0.5.0 - """ - assertions.assert_integer(levels=levels) - assertions.string_in_list(DITHER_METHODS, 'wand.image.DITHER_METHODS', - dither=dither) - dither_idx = DITHER_METHODS.index(dither) - return library.MagickPosterizeImage(self.wand, levels, dither_idx) - - @manipulative - @trap_exception - def quantize(self, number_colors, colorspace_type=None, - treedepth=0, dither=False, measure_error=False): - """`quantize` analyzes the colors within a sequence of images and - chooses a fixed number of colors to represent the image. The goal of - the algorithm is to minimize the color difference between the input and - output image while minimizing the processing time. - - :param number_colors: The target number of colors to reduce the image. - :type number_colors: :class:`numbers.Integral` - :param colorspace_type: Available value can be found - in the :const:`COLORSPACE_TYPES`. Defaults - :attr:`colorspace`. - :type colorspace_type: :class:`basestring` - :param treedepth: A value between ``0`` & ``8`` where ``0`` will - allow ImageMagick to calculate the optimal depth - with ``Log4(number_colors)``. Default value is ``0``. - :type treedepth: :class:`numbers.Integral` - :param dither: Perform dither operation between neighboring pixel - values. If using ImageMagick-6, this can be a value - of ``True``, or ``False``. With ImageMagick-7, use - a string from :const:`DITHER_METHODS`. Default - ``False``. - :type dither: :class:`bool`, or :class:`basestring` - :param measure_error: Include total quantization error of all pixels - in an image & quantized value. - :type measure_error: :class:`bool` - - .. versionadded:: 0.4.2 - - .. versionchanged:: 0.5.9 - Fixed ImageMagick-7 ``dither`` argument, and added keyword defaults. - """ - assertions.assert_integer(number_colors=number_colors) - if colorspace_type is None: - colorspace_type = self.colorspace - assertions.string_in_list(COLORSPACE_TYPES, - 'wand.image.COLORSPACE_TYPES', - colorspace_type=colorspace_type) - assertions.assert_integer(treedepth=treedepth) - if MAGICK_VERSION_NUMBER < 0x700: - assertions.assert_bool(dither=dither) - else: # pragma: no cover - if dither is False: - dither = 'no' - elif dither is True: - dither = 'riemersma' - assertions.string_in_list(DITHER_METHODS, - 'wand.image.DITHER_METHODS', - dither=dither) - dither = DITHER_METHODS.index(dither) - assertions.assert_bool(measure_error=measure_error) - return library.MagickQuantizeImage( - self.wand, number_colors, - COLORSPACE_TYPES.index(colorspace_type), - treedepth, dither, measure_error - ) - - @manipulative - @trap_exception - def random_threshold(self, low=0.0, high=1.0, channel=None): - """Performs a random dither to force a pixel into a binary black & - white state. Each color channel operarates independently from each - other. - - :param low: bottom threshold. Any pixel value below the given value - will be rendered "0", or no value. Given threshold value - can be between ``0.0`` & ``1.0``, or ``0`` & - :attr:`quantum_range`. - :type low: :class:`numbers.Real` - :param high: top threshold. Any pixel value above the given value - will be rendered as max quantum value. Given threshold - value can be between ``0.0`` & ``1.0``, or ``0`` & - :attr:`quantum_range`. - :type high: :class:`numbers.Real` - :param channel: Optional argument to apply dither to specific color - channel. See :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.7 - """ - assertions.assert_real(low=low, high=high) - if 0 < low <= 1.0: - low *= self.quantum_range - if 0 < high <= 1.0: - high *= self.quantum_range - if channel is None: - r = library.MagickRandomThresholdImage(self.wand, low, high) - else: - ch_channel = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickRandomThresholdImageChannel(self.wand, - ch_channel, - low, - high) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_channel) - r = library.MagickRandomThresholdImage(self.wand, low, high) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - def range_channel(self, channel='default_channels'): - """Calculate the minimum and maximum of quantum values in image. - - .. code:: python - - from wand.image import Image - - with Image(filename='input.jpg') as img: - minima, maxima = img.range_channel() - - :param channel: Select which color channel to evaluate. See - :const:`CHANNELS`. Default ``'default_channels'``. - :type channel: :class:`basestring` - :returns: Tuple of :attr:`minima` & :attr:`maxima` - values. Each value will be between 0.0 & - :attr:`quantum_range`. - :rtype: :class:`tuple` - - .. versionadded:: 0.5.3 - """ - ch_channel = self._channel_to_mask(channel) - min_color = ctypes.c_double(0.0) - max_color = ctypes.c_double(0.0) - if MAGICK_VERSION_NUMBER < 0x700: - library.MagickGetImageChannelRange(self.wand, ch_channel, - ctypes.byref(min_color), - ctypes.byref(max_color)) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - ch_channel) - library.MagickGetImageRange(self.wand, - ctypes.byref(min_color), - ctypes.byref(max_color)) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return min_color.value, max_color.value - - @manipulative - @trap_exception - def range_threshold(self, low_black=0.0, low_white=None, high_white=None, - high_black=None): - """Applies soft & hard thresholding. - - For a soft thresholding, parameters should be monotonically increasing: - - with Image(filename='text.png') as img: - img.range_threshold(0.2, 0.4, 0.6, 0.8) - - For a hard thresholding, parameters should be the same: - - with Image(filename='text.png') as img: - img.range_threshold(0.4, 0.4, 0.6, 0.6) - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :param low_black: Define the minimum threshold value. - :type low_black: :class:`numbers.Real` - :param low_white: Define the minimum threshold value. - :type low_white: :class:`numbers.Real` - :param high_white: Define the maximum threshold value. - :type high_white: :class:`numbers.Real` - :param high_black: Define the maximum threshold value. - :type high_black: :class:`numbers.Real` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickRangeThresholdImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - # Populate defaults to follow CLI behavior - if low_white is None: - low_white = low_black - if high_white is None: - high_white = low_white - if high_black is None: - high_black = high_white - assertions.assert_real(low_black=low_black, low_white=low_white, - high_white=high_white, high_black=high_black) - if 0 < low_black <= 1.0: - low_black *= self.quantum_range - if 0 < low_white <= 1.0: - low_white *= self.quantum_range - if 0 < high_white <= 1.0: - high_white *= self.quantum_range - if 0 < high_black <= 1.0: - high_black *= self.quantum_range - return library.MagickRangeThresholdImage(self.wand, - low_black, low_white, - high_white, high_black) - - @trap_exception - def read_mask(self, clip_mask=None): - """Sets the read mask where the gray values of the clip mask - are used to blend during composite operations. Call this method with - a ``None`` argument to clear any previously set masks. - - This method is also useful for :meth:`compare` method for limiting - region of interest. - - .. warning:: - This method is only available with ImageMagick-7. - - :param clip_mask: Image to reference as blend mask. - :type clip_mask: :class:`BaseImage` - - .. versionadded:: 0.5.7 - """ - r = False - ReadPixelMask = 0x000001 - if library.MagickSetImageMask is None: - raise WandLibraryVersionError('Method requires ImageMagick-7.') - else: # pragma: no cover - if clip_mask is None: - r = library.MagickSetImageMask(self.wand, ReadPixelMask, None) - elif isinstance(clip_mask, BaseImage): - r = library.MagickSetImageMask(self.wand, ReadPixelMask, - clip_mask.wand) - return r - - def region(self, width=None, height=None, x=None, y=None, gravity=None): - """Extract an area of the image. This is the same as :meth:`crop`, - but returns a new instance of :class:`Image` without altering the - original source image. - - .. code-block:: python - - from wand.image import Image - - with Image(filename='input.jpg') as img: - with img.region(width=100, height=100, x=0, y=0) as area: - pass - - :param width: Area size on the X-axis. Default value is - the image's :attr:`page_width`. - :type width: :class:`numbers.Integral` - :param height: Area size on the Y-axis.Default value is - the image's :attr:`page_height`. - :type height: :class:`numbers.Integral` - :param x: X-axis offset. This number can be negative. Default value is - the image's :attr:`page_x`. - :type x: :class:`numbers.Integral` - :param y: Y-axis offset. This number can be negative. Default value is - the image's :attr:`page_y`. - :type y: :class:`numbers.Integral` - :param region: Helper attribute to set ``x`` & ``y`` offset. See - :const:`GRAVITY_TYPES`. - :type region: :class:`basestring` - :returns: New instance of Wand. - :rtype: :class:`Image` - - .. versionadded:: 0.6.8 - """ - ow, oh, ox, oy = self.page - if width is None: - width = ow - if height is None: - height = oh - assertions.assert_unsigned_integer(width=width, height=height) - if gravity is not None: - if x is not None or y is not None: - raise ValueError('x & y can not be used with gravity.') - y, x = self._gravity_to_offset(gravity, width, height) - if x is None: - x = ox - if y is None: - y = oy - assertions.assert_integer(x=x, y=y) - new_wand = library.MagickGetImageRegion(self.wand, width, height, x, y) - if not new_wand: - self.raise_exception() - return Image(BaseImage(new_wand)) - - @manipulative - @trap_exception - def remap(self, affinity=None, method='no'): - """Rebuild image palette with closest color from given affinity image. - - :see: Example of :ref:`remap`. - - :param affinity: reference image. - :type affinity: :class:`BaseImage` - :param method: dither method. See :const:`DITHER_METHODS`. - Default is ``'no'`` dither. - :type method: :class:`basestring` - - .. versionadded:: 0.5.3 - """ - if not isinstance(affinity, BaseImage): - raise TypeError('Expecting affinity to be a BaseImage, not ' + - repr(affinity)) - assertions.string_in_list(DITHER_METHODS, 'wand.image.DITHER_METHODS', - method=method) - method_idx = DITHER_METHODS.index(method) - return library.MagickRemapImage(self.wand, affinity.wand, method_idx) - - @manipulative - @trap_exception - def resample(self, x_res=None, y_res=None, filter='undefined', blur=1): - """Adjust the number of pixels in an image so that when displayed at - the given Resolution or Density the image will still look the same size - in real world terms. - - .. note:: - - This method will automatically :meth:`coalesce` & resample all - frames in a GIF animation. For other image formats, - :meth:`resample` will only effect the current image in the stack. - Use :meth:`iterator_reset` & :meth:`iterator_next` to traverse - the image stack to resample all images in a multi-layer document. - - .. code:: - - with Image(filename='input.tiff') as img: - img.iterator_reset() - while True: - img.resample(128, 128) - if not img.iterator_next(): - break - - :param x_res: the X resolution (density) in the scaled image. default - is the original resolution. - :type x_res: :class:`numbers.Real` - :param y_res: the Y resolution (density) in the scaled image. default - is the original resolution. - :type y_res: :class:`numbers.Real` - :param filter: a filter type to use for resizing. choose one in - :const:`FILTER_TYPES`. default is ``'undefined'`` - which means IM will try to guess best one to use. - :type filter: :class:`basestring`, :class:`numbers.Integral` - :param blur: the blur factor where > 1 is blurry, < 1 is sharp. - default is 1 - :type blur: :class:`numbers.Real` - - .. versionadded:: 0.4.5 - """ - if x_res is None: - x_res, _ = self.resolution - if y_res is None: - _, y_res = self.resolution - assertions.assert_real(x_res=x_res, y_res=y_res, blur=blur) - if x_res < 1: - raise ValueError('x_res must be a Real number, not ' + - repr(x_res)) - elif y_res < 1: - raise ValueError('y_res must be a Real number, not ' + - repr(y_res)) - elif not isinstance(filter, (string_type, numbers.Integral)): - raise TypeError('filter must be one string defined in wand.image.' - 'FILTER_TYPES or an integer, not ' + repr(filter)) - if isinstance(filter, string_type): - try: - filter = FILTER_TYPES.index(filter) - except IndexError: - raise ValueError(repr(filter) + ' is an invalid filter type; ' - 'choose on in ' + repr(FILTER_TYPES)) - elif (isinstance(filter, numbers.Integral) and - not (0 <= filter < len(FILTER_TYPES))): - raise ValueError(repr(filter) + ' is an invalid filter type') - blur = ctypes.c_double(float(blur)) - if self.animation: - self.wand = library.MagickCoalesceImages(self.wand) - self.reset_sequence() - library.MagickSetLastIterator(self.wand) - n = library.MagickGetIteratorIndex(self.wand) - library.MagickResetIterator(self.wand) - for i in xrange(n + 1): - library.MagickSetIteratorIndex(self.wand, i) - r = library.MagickResampleImage(self.wand, x_res, y_res, - filter, blur) - else: - r = library.MagickResampleImage(self.wand, x_res, y_res, - filter, blur) - return r - - def reset_coords(self): - """Reset the coordinate frame of the image so to the upper-left corner - is (0, 0) again (crop and rotate operations change it). - - .. versionadded:: 0.2.0 - - """ - library.MagickResetImagePage(self.wand, None) - - def reset_sequence(self): - """Abstract method prototype. - See :meth:`wand.image.Image.reset_sequence()`. - - .. versionadded:: 0.6.0 - """ - pass - - @manipulative - @trap_exception - def resize(self, width=None, height=None, filter='undefined', blur=1): - """Resizes the image. - - :param width: the width in the scaled image. default is the original - width - :type width: :class:`numbers.Integral` - :param height: the height in the scaled image. default is the original - height - :type height: :class:`numbers.Integral` - :param filter: a filter type to use for resizing. choose one in - :const:`FILTER_TYPES`. default is ``'undefined'`` - which means IM will try to guess best one to use - :type filter: :class:`basestring`, :class:`numbers.Integral` - :param blur: the blur factor where > 1 is blurry, < 1 is sharp. - default is 1 - :type blur: :class:`numbers.Real` - - .. versionchanged:: 0.2.1 - The default value of ``filter`` has changed from ``'triangle'`` - to ``'undefined'`` instead. - - .. versionchanged:: 0.1.8 - The ``blur`` parameter changed to take :class:`numbers.Real` - instead of :class:`numbers.Rational`. - - .. versionadded:: 0.1.1 - - """ - if width is None: - width = self.width - if height is None: - height = self.height - assertions.assert_counting_number(width=width, height=height) - assertions.assert_real(blur=blur) - if not isinstance(filter, (string_type, numbers.Integral)): - raise TypeError('filter must be one string defined in wand.image.' - 'FILTER_TYPES or an integer, not ' + repr(filter)) - if isinstance(filter, string_type): - try: - filter = FILTER_TYPES.index(filter) - except IndexError: - raise ValueError(repr(filter) + ' is an invalid filter type; ' - 'choose on in ' + repr(FILTER_TYPES)) - elif (isinstance(filter, numbers.Integral) and - not (0 <= filter < len(FILTER_TYPES))): - raise ValueError(repr(filter) + ' is an invalid filter type') - blur = ctypes.c_double(float(blur)) - if self.animation: - self.wand = library.MagickCoalesceImages(self.wand) - self.reset_sequence() - library.MagickSetLastIterator(self.wand) - n = library.MagickGetIteratorIndex(self.wand) - library.MagickResetIterator(self.wand) - for i in xrange(n + 1): - library.MagickSetIteratorIndex(self.wand, i) - r = library.MagickResizeImage(self.wand, width, height, - filter, blur) - library.MagickSetSize(self.wand, width, height) - else: - r = library.MagickResizeImage(self.wand, width, height, - filter, blur) - library.MagickSetSize(self.wand, width, height) - return r - - @manipulative - @trap_exception - def roll(self, x=0, y=0): - """Shifts all pixels over by an X/Y offset. - - :param x: Number of columns to roll over. Negative value will roll - pixels from right-to-left, and positive value will roll - pixels from left-to-right. Default value: ``0``. - :type x: :class:`numbers.Integral` - :param y: Number of rows to roll over. Negative value will roll - pixels from bottom-to-top, and positive value will roll - pixels from top-to-bottm. Default value: ``0``. - :type y: :class:`numbers.Integral` - - .. versionadded:: 0.6.8 - """ - assertions.assert_integer(x=x, y=y) - return library.MagickRollImage(self.wand, x, y) - - @manipulative - @trap_exception - def rotate(self, degree, background=None, reset_coords=True): - """Rotates the image right. It takes a ``background`` color - for ``degree`` that isn't a multiple of 90. - - :see: Example of :ref:`rotate`. - - :param degree: a degree to rotate. multiples of 360 affect nothing - :type degree: :class:`numbers.Real` - :param background: an optional background color. - default is transparent - :type background: :class:`wand.color.Color` - :param reset_coords: optional flag. If set, after the rotation, the - coordinate frame will be relocated to the upper-left corner of - the new image. By default is `True`. - :type reset_coords: :class:`bool` - - .. versionadded:: 0.2.0 - The ``reset_coords`` parameter. - - .. versionadded:: 0.1.8 - - """ - if background is None: - background = Color('transparent') - elif isinstance(background, string_type): - background = Color(background) - assertions.assert_color(background=background) - assertions.assert_real(degree=degree) - with background: - if self.animation: - self.wand = library.MagickCoalesceImages(self.wand) - self.reset_sequence() - library.MagickSetLastIterator(self.wand) - n = library.MagickGetIteratorIndex(self.wand) - library.MagickResetIterator(self.wand) - for i in xrange(0, n + 1): - library.MagickSetIteratorIndex(self.wand, i) - result = library.MagickRotateImage(self.wand, - background.resource, - degree) - if reset_coords: - library.MagickResetImagePage(self.wand, None) - else: - result = library.MagickRotateImage(self.wand, - background.resource, - degree) - if reset_coords: - self.reset_coords() - return result - - @manipulative - @trap_exception - def rotational_blur(self, angle=0.0, channel=None): - """Blur an image in a radius around the center of an image. - - .. warning:: Requires ImageMagick-6.8.8 or greater. - - :see: Example of :ref:`rotational_blur`. - - :param angle: Degrees of rotation to blur with. - :type angle: :class:`numbers.Real` - :param channel: Optional channel to apply the effect against. See - :const:`CHANNELS` for a list of possible values. - :type channel: :class:`basestring` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.4 - """ - if not library.MagickRotationalBlurImage: # pragma: no cover - msg = ("Method `rotational_blur` not available on installed " - "version of ImageMagick library. ") - raise WandLibraryVersionError(msg) - assertions.assert_real(angle=angle) - if channel: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickRotationalBlurImageChannel(self.wand, - channel_ch, - angle) - else: # pragma: no cover - channel_mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickRotationalBlurImage(self.wand, angle) - library.MagickSetImageChannelMask(self.wand, channel_mask) - else: - r = library.MagickRotationalBlurImage(self.wand, angle) - return r - - @manipulative - @trap_exception - def sample(self, width=None, height=None): - """Resizes the image by sampling the pixels. It's basically quicker - than :meth:`resize()` except less quality as a trade-off. - - :param width: the width in the scaled image. default is the original - width - :type width: :class:`numbers.Integral` - :param height: the height in the scaled image. default is the original - height - :type height: :class:`numbers.Integral` - - .. versionadded:: 0.3.4 - - """ - if width is None: - width = self.width - if height is None: - height = self.height - assertions.assert_counting_number(width=width, height=height) - if self.animation: - self.wand = library.MagickCoalesceImages(self.wand) - self.reset_sequence() - library.MagickSetLastIterator(self.wand) - n = library.MagickGetIteratorIndex(self.wand) - library.MagickResetIterator(self.wand) - for i in xrange(n + 1): - library.MagickSetIteratorIndex(self.wand, i) - r = library.MagickSampleImage(self.wand, width, height) - library.MagickSetSize(self.wand, width, height) - else: - r = library.MagickSampleImage(self.wand, width, height) - library.MagickSetSize(self.wand, width, height) - return bool(r) - - @manipulative - @trap_exception - def scale(self, columns=1, rows=1): - """Increase image size by scaling each pixel value by given ``columns`` - and ``rows``. - - :param columns: The number of columns, in pixels, to scale the image - horizontally. - :type columns: :class:`numbers.Integral` - :param rows: The number of rows, in pixels, to scale the image - vertically. - :type rows: :class:`numbers.Integral` - - .. versionadded:: 0.5.7 - """ - assertions.assert_counting_number(columns=columns, rows=rows) - return library.MagickScaleImage(self.wand, columns, rows) - - @manipulative - @trap_exception - def selective_blur(self, radius=0.0, sigma=0.0, threshold=0.0, - channel=None): - """Blur an image within a given threshold. - - For best effects, use a value between 10% and 50% of - :attr:`quantum_range` - - .. code:: - - from wand.image import Image - - with Image(filename='photo.jpg') as img: - # Apply 8x3 blur with a 10% threshold - img.selective_blur(8.0, 3.0, 0.1 * img.quantum_range) - - :see: Example of :ref:`selective_blur`. - - :param radius: Size of gaussian aperture. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of gaussian operator. - :type sigma: :class:`numbers.Real` - :param threshold: Only pixels within contrast threshold are effected. - Value should be between ``0.0`` and - :attr:`quantum_range`. - :type threshold: :class:`numbers.Real` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - assertions.assert_real(radius=radius, sigma=sigma, threshold=threshold) - if channel is None: - r = library.MagickSelectiveBlurImage(self.wand, - radius, - sigma, - threshold) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSelectiveBlurImageChannel(self.wand, - channel_ch, - radius, - sigma, - threshold) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickSelectiveBlurImage(self.wand, - radius, - sigma, - threshold) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def sepia_tone(self, threshold=0.8): - """Creates a Sepia Tone special effect similar to a darkroom chemical - toning. - - :see: Example of :ref:`sepia_tone`. - - :param threshold: The extent of the toning. Value can be between ``0`` - & :attr:`quantum_range`, or ``0`` & ``1.0``. - Default value is ``0.8`` or "80%". - :type threshold: :class:`numbers.Real` - - .. versionadded:: 0.5.7 - """ - assertions.assert_real(threshold=threshold) - if 0.0 < threshold <= 1.0: - threshold *= self.quantum_range - return library.MagickSepiaToneImage(self.wand, threshold) - - @manipulative - @trap_exception - def shade(self, gray=False, azimuth=0.0, elevation=0.0): - """Creates a 3D effect by simulating a light from an - elevated angle. - - :see: Example of :ref:`shade`. - - :param gray: Isolate the effect on pixel intensity. - Default is False. - :type gray: :class:`bool` - :param azimuth: Angle from x-axis. - :type azimuth: :class:`numbers.Real` - :param elevation: Amount of pixels from the z-axis. - :type elevation: :class:`numbers.Real` - - .. versionadded:: 0.5.0 - """ - assertions.assert_real(azimuth=azimuth, elevation=elevation) - return library.MagickShadeImage(self.wand, gray, - azimuth, elevation) - - @manipulative - @trap_exception - def shadow(self, alpha=0.0, sigma=0.0, x=0, y=0): - """Generates an image shadow. - - :param alpha: Ratio of transparency. - :type alpha: :class:`numbers.Real` - :param sigma: Standard deviation of the gaussian filter. - :type sigma: :class:`numbers.Real` - :param x: x-offset. - :type x: :class:`numbers.Integral` - :param y: y-offset. - :type y: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - """ - assertions.assert_real(alpha=alpha, sigma=sigma) - assertions.assert_integer(x=x, y=y) - return library.MagickShadowImage(self.wand, alpha, sigma, x, y) - - @manipulative - @trap_exception - def sharpen(self, radius=0.0, sigma=0.0, channel=None): - """Applies a gaussian effect to enhance the sharpness of an - image. - - .. note:: - - For best results, ensure ``radius`` is larger than - ``sigma``. - - Defaults values of zero will have ImageMagick attempt - to auto-select suitable values. - - :see: Example of :ref:`sharpen`. - - :param radius: size of gaussian aperture. - :type radius: :class:`numbers.Real` - :param sigma: Standard deviation of the gaussian filter. - :type sigma: :class:`numbers.Real` - :param channel: Optional color channel to target. See - :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.0 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - assertions.assert_real(radius=radius, sigma=sigma) - if channel is None: - r = library.MagickSharpenImage(self.wand, radius, sigma) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSharpenImageChannel(self.wand, - channel_ch, - radius, sigma) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickSharpenImage(self.wand, radius, sigma) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def shave(self, columns=0, rows=0): - """Remove pixels from the edges. - - :param columns: amount to shave off both sides of the x-axis. - :type columns: :class:`numbers.Integral` - :param rows: amount to shave off both sides of the y-axis. - :type rows: :class:`numbers.Integral` - - .. versionadded:: 0.5.0 - """ - assertions.assert_integer(columns=columns, row=rows) - return library.MagickShaveImage(self.wand, columns, rows) - - @manipulative - @trap_exception - def shear(self, background='WHITE', x=0.0, y=0.0): - """Shears the image to create a parallelogram, and fill the space - created with a ``background`` color. - - :param background: Color to fill the void created by shearing the - image. - :type background: :class:`wand.color.Color` - :param x: Slide the image along the X-axis. - :type x: :class:`numbers.Real` - :param y: Slide the image along the Y-axis. - :type y: :class:`numbers.Real` - - .. versionadded:: 0.5.4 - """ - if isinstance(background, string_type): - background = Color(background) - assertions.assert_color(background=background) - assertions.assert_real(x=x, y=y) - with background: - r = library.MagickShearImage(self.wand, background.resource, x, y) - return r - - @manipulative - @trap_exception - def sigmoidal_contrast(self, sharpen=True, strength=0.0, midpoint=0.0, - channel=None): - """Modifies the contrast of the image by applying non-linear sigmoidal - algorithm. - - .. code:: python - - with Image(filename='photo.jpg') as img: - img.sigmoidal_contrast(sharpen=True, - strength=3, - midpoint=0.65 * img.quantum_range) - - :param sharpen: Increase the contrast when ``True`` (default), else - reduces contrast. - :type sharpen: :class:`bool` - :param strength: How much to adjust the contrast. Where a value of - ``0.0`` has no effect, ``3.0`` is typical, and - ``20.0`` is extreme. - :type strength: :class:`numbers.Real` - :param midpoint: Normalized value between `0.0` & :attr:`quantum_range` - :type midpoint: :class:`numbers.Real` - :param channel: Optional color channel to target. See - :const:`CHANNELS`. - :type channel: :class:`basestring` - - .. versionadded:: 0.5.4 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - assertions.assert_bool(sharpen=sharpen) - assertions.assert_real(strength=strength, midpoint=midpoint) - if channel is None: - r = library.MagickSigmoidalContrastImage(self.wand, - sharpen, - strength, - midpoint) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSigmoidalContrastImageChannel( - self.wand, channel_ch, sharpen, strength, midpoint - ) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickSigmoidalContrastImage(self.wand, - sharpen, - strength, - midpoint) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - def similarity(self, reference, threshold=0.0, - metric='undefined'): - """Scan image for best matching ``reference`` image, and - return location & similarity. - - Use parameter ``threshold`` to stop subimage scanning if the matching - similarity value is below the given value. This is the same as the CLI - ``-similarity-threshold`` option. - - This method will always return a location & the lowest computed - similarity value. Users are responsible for checking the similarity - value to determine if a matching location is valid. Traditionally, a - similarity value greater than `0.3183099` is considered dissimilar. - - .. code:: python - - from wand.image import Image - - dissimilarity_threshold = 0.318 - similarity_threshold = 0.05 - with Image(filename='subject.jpg') as img: - with Image(filename='object.jpg') as reference: - location, diff = img.similarity(reference, - similarity_threshold) - if diff > dissimilarity_threshold: - print('Images too dissimilar to match') - elif diff <= similarity_threshold: - print('First match @ {left}x{top}'.format(**location)) - else: - print('Best match @ {left}x{top}'.format(**location)) - - .. warning:: - - This operation can be slow to complete. - - :param reference: Image to search for. - :type reference: :class:`wand.image.Image` - :param threshold: Stop scanning if reference similarity is - below given threshold. Value can be between ``0.0`` - and :attr:`quantum_range`. Default is ``0.0``. - :type threshold: :class:`numbers.Real` - :param metric: specify which comparison algorithm to use. See - :const:`COMPARE_METRICS` for a list of values. - Only used by ImageMagick-7. - :type metric: :class:`basestring` - :returns: List of location & similarity value. Location being a - dictionary of ``width``, ``height``, ``left``, & ``top``. - The similarity value is the compare distance, so a value of - ``0.0`` means an exact match. - :rtype: :class:`tuple` (:class:`dict`, :class:`numbers.Real`) - - .. versionadded:: 0.5.4 - - has been added. - """ - assertions.assert_real(threshold=threshold) - if not isinstance(reference, BaseImage): - raise TypeError('reference must be in instance of ' - 'wand.image.Image, not ' + repr(reference)) - rio = RectangleInfo(0, 0, 0, 0) - diff = ctypes.c_double(0.0) - if MAGICK_VERSION_NUMBER < 0x700: - artifact_value = binary(str(threshold)) # FIXME - library.MagickSetImageArtifact(self.wand, - b'compare:similarity-threshold', - artifact_value) - r = library.MagickSimilarityImage(self.wand, - reference.wand, - ctypes.byref(rio), - ctypes.byref(diff)) - else: # pragma: no cover - assertions.string_in_list(COMPARE_METRICS, - 'wand.image.COMPARE_METRICS', - metric=metric) - metric_idx = COMPARE_METRICS.index(metric) - r = library.MagickSimilarityImage(self.wand, - reference.wand, - metric_idx, - threshold, - ctypes.byref(rio), - ctypes.byref(diff)) - if not r: # pragma: no cover - self.raise_exception() - else: - r = library.DestroyMagickWand(r) - location = dict(width=rio.width, height=rio.height, - top=rio.y, left=rio.x) - return (location, diff.value) - - @manipulative - @trap_exception - def sketch(self, radius=0.0, sigma=0.0, angle=0.0): - """Simulates a pencil sketch effect. For best results, ``radius`` - value should be larger than ``sigma``. - - :see: Example of :ref:`sketch`. - - :param radius: size of Gaussian aperture. - :type radius: :class:`numbers.Real` - :param sigma: standard deviation of the Gaussian operator. - :type sigma: :class:`numbers.Real` - :param angle: direction of blur. - :type angle: :class:`numbers.Real` - - .. versionadded:: 0.5.3 - """ - assertions.assert_real(radius=radius, sigma=sigma, angle=angle) - return library.MagickSketchImage(self.wand, radius, sigma, angle) - - @trap_exception - def smush(self, stacked=False, offset=0): - """Appends all images together. Similar behavior to :meth:`concat`, - but with an optional offset between images. - - :param stacked: If True, will join top-to-bottom. If False, join images - from left-to-right (default). - :type stacked: :class:`bool` - :param offset: Minimum space (in pixels) between each join. - :type offset: :class:`numbers.Integral` - - .. versionadded:: 0.5.3 - """ - assertions.assert_integer(offset=offset) - library.MagickResetIterator(self.wand) - result = library.MagickSmushImages(self.wand, bool(stacked), offset) - if result: - self.wand = result - self.reset_sequence() - return bool(result) - - @manipulative - @trap_exception - def solarize(self, threshold=0.0, channel=None): - """Simulates extreme overexposure. - - :see: Example of :ref:`solarize`. - - :param threshold: between ``0.0`` and :attr:`quantum_range`. - :type threshold: :class:`numbers.Real` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.5 - Added ``channel`` argument. - """ - assertions.assert_real(threshold=threshold) - if channel is None: - r = library.MagickSolarizeImage(self.wand, threshold) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSolarizeImageChannel(self.wand, - channel_ch, - threshold) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickSolarizeImage(self.wand, threshold) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def sparse_color(self, method, colors, channel_mask=0x7): - """Interpolates color values between points on an image. - - The ``colors`` argument should be a dict mapping - :class:`~wand.color.Color` keys to coordinate tuples. - - For example:: - - from wand.color import Color - from wand.image import Image - - colors = { - Color('RED'): (10, 50), - Color('YELLOW'): (174, 32), - Color('ORANGE'): (74, 123) - } - with Image(filename='input.png') as img: - img.sparse_color('bilinear', colors) - - The available interpolate methods are: - - - ``'barycentric'`` - - ``'bilinear'`` - - ``'shepards'`` - - ``'voronoi'`` - - ``'inverse'`` - - ``'manhattan'`` - - You can control which color channels are effected by building a custom - channel mask. For example:: - - from wand.image import Image, CHANNELS - - with Image(filename='input.png') as img: - colors = { - img[50, 50]: (50, 50), - img[100, 50]: (100, 50), - img[50, 75]: (50, 75), - img[100, 100]: (100, 100) - } - # Only apply Voronoi to Red & Alpha channels - mask = CHANNELS['red'] | CHANNELS['alpha'] - img.sparse_color('voronoi', colors, channel_mask=mask) - - :param method: Interpolate method. See :const:`SPARSE_COLOR_METHODS` - :type method: :class:`basestring` - :param colors: A dictionary of :class:`~wand.color.Color` keys mapped - to an (x, y) coordinate tuple. - :type colors: :class:`abc.Mapping` - { :class:`~wand.color.Color`: (int, int) } - :param channel_mask: Isolate specific color channels to apply - interpolation. Default to RGB channels. - :type channel_mask: :class:`numbers.Integral` - - .. versionadded:: 0.5.3 - """ - assertions.string_in_list(SPARSE_COLOR_METHODS, - 'wand.image.SPARSE_COLOR_METHODS', - method=method) - if not isinstance(colors, abc.Mapping): - raise TypeError('Colors must be a dict, not' + repr(colors)) - assertions.assert_unsigned_integer(channel_mask=channel_mask) - method_idx = SPARSE_COLOR_METHODS[method] - arguments = list() - for color, point in colors.items(): - if isinstance(color, string_type): - color = Color(color) - x, y = point - arguments.append(x) - arguments.append(y) - with color as c: - if channel_mask & CHANNELS['red']: - arguments.append(c.red) - if channel_mask & CHANNELS['green']: - arguments.append(c.green) - if channel_mask & CHANNELS['blue']: - arguments.append(c.blue) - if channel_mask & CHANNELS['alpha']: - arguments.append(c.alpha) - argc = len(arguments) - args = (ctypes.c_double * argc)(*arguments) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSparseColorImage(self.wand, - channel_mask, - method_idx, - argc, - args) - else: # pragma: no cover - # Set active channel, and capture mask to restore. - channel_mask = library.MagickSetImageChannelMask(self.wand, - channel_mask) - r = library.MagickSparseColorImage(self.wand, - method_idx, - argc, - args) - # Restore original state of channels - library.MagickSetImageChannelMask(self.wand, channel_mask) - return r - - @manipulative - @trap_exception - def splice(self, width=None, height=None, x=None, y=None, gravity=None): - """Partitions image by splicing a ``width`` x ``height`` rectangle at - (``x``, ``y``) offset coordinate. The space inserted will be replaced - by the :attr:`background_color` value. - - :param width: number of pixel columns. - :type width: :class:`numbers.Integral` - :param height: number of pixel rows. - :type height: :class:`numbers.Integral` - :param x: offset on the X-axis. - :type x: :class:`numbers.Integral` - :param y: offset on the Y-axis. - :type y: :class:`numbers.Integral` - - .. versionadded:: 0.5.3 - """ - ow, oh = self.size - if width is None: - width = ow - if height is None: - height = oh - assertions.assert_unsigned_integer(width=width, height=height) - if gravity is None: - if x is None: - x = 0 - if y is None: - y = 0 - else: - if x is not None or y is not None: - raise ValueError('x & y can not be used with gravity.') - y, x = self._gravity_to_offset(gravity, width, height) - assertions.assert_integer(x=x, y=y) - return library.MagickSpliceImage(self.wand, width, height, x, y) - - @manipulative - @trap_exception - def spread(self, radius=0.0, method='undefined'): - """Randomly displace pixels within a defined radius. - - :see: Example of :ref:`spread`. - - :param radius: Distance a pixel can be displaced from source. Default - value is ``0.0``, which will allow ImageMagick to auto - select a radius. - :type radius: :class:`numbers.Real` - :param method: Interpolation method. Only available with ImageMagick-7. - See :const:`PIXEL_INTERPOLATE_METHODS`. - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.7 - Added default value to ``radius``. - """ - assertions.assert_real(radius=radius) - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - method=method) - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSpreadImage(self.wand, radius) - else: # pragma: no cover - r = library.MagickSpreadImage(self.wand, method_idx, radius) - return r - - @manipulative - @trap_exception - def statistic(self, stat='undefined', width=None, height=None, - channel=None): - """Replace each pixel with the statistic results from neighboring pixel - values. The ``width`` & ``height`` defines the size, or aperture, of - the neighboring pixels. - - :see: Example of :ref:`statistic`. - - :param stat: The type of statistic to calculate. See - :const:`STATISTIC_TYPES`. - :type stat: :class:`basestring` - :param width: The size of neighboring pixels on the X-axis. - :type width: :class:`numbers.Integral` - :param height: The size of neighboring pixels on the Y-axis. - :type height: :class:`numbers.Integral` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.5.3 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument. - """ - assertions.string_in_list(STATISTIC_TYPES, - 'wand.image.STATISTIC_TYPES', - statistic=stat) - assertions.assert_integer(width=width, height=height) - stat_idx = STATISTIC_TYPES.index(stat) - if channel is None: - r = library.MagickStatisticImage(self.wand, stat_idx, - width, height) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickStatisticImageChannel(self.wand, - channel_ch, - stat_idx, - width, - height) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, - channel_ch) - r = library.MagickStatisticImage(self.wand, stat_idx, - width, height) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def stegano(self, watermark, offset=0): - """Hide a digital watermark of an image within the image. - - .. code-block:: python - - from wand.image import Image - - # Embed watermark - with Image(filename='source.png') as img: - with Image(filename='gray_watermark.png') as watermark: - print('watermark size (for recovery)', watermark.size) - img.stegano(watermark) - img.save(filename='public.png') - - # Recover watermark - with Image(width=w, height=h, pseudo='stegano:public.png') as img: - img.save(filename='recovered_watermark.png') - - :param watermark: Image to hide within image. - :type watermark: :class:`wand.image.Image` - :param offset: Start embedding image after a number of pixels. - :type offset: :class:`numbers.Integral` - - .. versionadded:: 0.5.4 - """ - if not isinstance(watermark, BaseImage): - raise TypeError('Watermark image must be in instance of ' - 'wand.image.Image, not ' + repr(watermark)) - assertions.assert_integer(offset=offset) - new_wand = library.MagickSteganoImage(self.wand, watermark.wand, - offset) - if new_wand: - self.wand = new_wand - self.reset_sequence() - return bool(new_wand) - - @trap_exception - def strip(self): - """Strips an image of all profiles and comments. - - .. versionadded:: 0.2.0 - """ - return library.MagickStripImage(self.wand) - - @manipulative - @trap_exception - def swirl(self, degree=0.0, method="undefined"): - """Swirls pixels around the center of the image. The larger the degree - the more pixels will be effected. - - :see: Example of :ref:`swirl`. - - :param degree: Defines the amount of pixels to be effected. Value - between ``-360.0`` and ``360.0``. - :type degree: :class:`numbers.Real` - :param method: Controls interpolation of the effected pixels. Only - available for ImageMagick-7. See - :const:`PIXEL_INTERPOLATE_METHODS`. - :type method: :class:`basestring` - - .. versionadded:: 0.5.7 - """ - assertions.assert_real(degree=degree) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickSwirlImage(self.wand, degree) - else: # pragma: no cover - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - method=method) - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - r = library.MagickSwirlImage(self.wand, degree, method_idx) - return r - - @manipulative - @trap_exception - def texture(self, tile): - """Repeat tile-image across the width & height of the image. - - .. code:: python - - from wand.image import Image - - with Image(width=100, height=100) as canvas: - with Image(filename='tile.png') as tile: - canvas.texture(tile) - canvas.save(filename='output.png') - - :param tile: image to repeat across canvas. - :type tile: :class:`Image ` - - .. versionadded:: 0.5.4 - """ - if not isinstance(tile, BaseImage): - raise TypeError('Tile image must be an instance of ' - 'wand.image.Image, not ' + repr(tile)) - r = library.MagickTextureImage(self.wand, tile.wand) - if r: - self.wand = r - return bool(r) - - @manipulative - @trap_exception - def threshold(self, threshold=0.5, channel=None): - """Changes the value of individual pixels based on the intensity - of each pixel compared to threshold. The result is a high-contrast, - two color image. It manipulates the image in place. - - :param threshold: threshold as a factor of quantum. A normalized float - between ``0.0`` and ``1.0``. - :type threshold: :class:`numbers.Real` - :param channel: the channel type. available values can be found - in the :const:`CHANNELS` mapping. If ``None``, - threshold all channels. - :type channel: :class:`basestring` - - .. versionadded:: 0.3.10 - - """ - assertions.assert_real(threshold=threshold) - threshold *= self.quantum_range + 1 - if channel is None: - r = library.MagickThresholdImage(self.wand, threshold) - else: - ch_const = self._channel_to_mask(channel) - r = library.MagickThresholdImageChannel( - self.wand, ch_const, - threshold - ) - return r - - @manipulative - @trap_exception - def thumbnail(self, width=None, height=None): - """Changes the size of an image to the given dimensions and removes any - associated profiles. The goal is to produce small low cost thumbnail - images suited for display on the web. - - :param width: the width in the scaled image. default is the original - width - :type width: :class:`numbers.Integral` - :param height: the height in the scaled image. default is the original - height - :type height: :class:`numbers.Integral` - - .. versionadded:: 0.5.4 - """ - if width is None: - width = self.width - if height is None: - height = self.height - assertions.assert_unsigned_integer(width=width, height=height) - return library.MagickThumbnailImage(self.wand, width, height) - - @manipulative - @trap_exception - def tint(self, color=None, alpha=None): - """Applies a color vector to each pixel in the image. - - :see: Example of :ref:`tint`. - - :param color: Color to calculate midtone. - :type color: :class:`~wand.color.Color` - :param alpha: Determine how to blend. - :type alpha: :class:`~wand.color.Color` - - .. versionadded:: 0.5.3 - """ - if isinstance(color, string_type): - color = Color(color) - if isinstance(alpha, string_type): - alpha = Color(alpha) - assertions.assert_color(color=color, alpha=alpha) - with color: - with alpha: - r = library.MagickTintImage(self.wand, - color.resource, - alpha.resource) - return r - - @manipulative - @trap_exception - def transform(self, crop='', resize=''): - """Transforms the image using :c:func:`MagickTransformImage`, - which is a convenience function accepting geometry strings to - perform cropping and resizing. Cropping is performed first, - followed by resizing. Either or both arguments may be omitted - or given an empty string, in which case the corresponding action - will not be performed. Geometry specification strings are - defined as follows: - - A geometry string consists of a size followed by an optional offset. - The size is specified by one of the options below, - where **bold** terms are replaced with appropriate integer values: - - **scale**\\ ``%`` - Height and width both scaled by specified percentage. - - **scale-x**\\ ``%x``\\ \\ **scale-y**\\ ``%`` - Height and width individually scaled by specified percentages. - Only one % symbol is needed. - - **width** - Width given, height automagically selected to preserve aspect ratio. - - ``x``\\ \\ **height** - Height given, width automagically selected to preserve aspect ratio. - - **width**\\ ``x``\\ **height** - Maximum values of width and height given; aspect ratio preserved. - - **width**\\ ``x``\\ **height**\\ ``!`` - Width and height emphatically given; original aspect ratio ignored. - - **width**\\ ``x``\\ **height**\\ ``>`` - Shrinks images with dimension(s) larger than the corresponding - width and/or height dimension(s). - - **width**\\ ``x``\\ **height**\\ ``<`` - Enlarges images with dimensions smaller than the corresponding - width and/or height dimension(s). - - **area**\\ ``@`` - Resize image to have the specified area in pixels. - Aspect ratio is preserved. - - **X**\\ ``:``\\ **Y** - Resize at a given aspect ratio. Common aspect ratios may - include ``4:3`` for video/tv, ``3:2`` for 35mm film, ``16:9`` for - HDTV, and ``2.39:1`` for cinema. Aspect ratio can be used with the - crop parameter, but is only available with ImageMagick version 7.0.8 - or greater. - - The offset, which only applies to the cropping geometry string, - is given by ``{+-}``\\ **x**\\ ``{+-}``\\ **y**\\ , that is, - one plus or minus sign followed by an **x** offset, - followed by another plus or minus sign, followed by a **y** offset. - Offsets are in pixels from the upper left corner of the image. - Negative offsets will cause the corresponding number of pixels to - be removed from the right or bottom edge of the image, meaning the - cropped size will be the computed size minus the absolute value - of the offset. - - For example, if you want to crop your image to 300x300 pixels - and then scale it by 2x for a final size of 600x600 pixels, - you can call:: - - image.transform('300x300', '200%') - - This method is a fairly thin wrapper for the C API, and does not - perform any additional checking of the parameters except insofar as - verifying that they are of the correct type. Thus, like the C - API function, the method is very permissive in terms of what - it accepts for geometry strings; unrecognized strings and - trailing characters will be ignored rather than raising an error. - - :param crop: A geometry string defining a subregion of the image - to crop to - :type crop: :class:`basestring` - :param resize: A geometry string defining the final size of the image - :type resize: :class:`basestring` - - .. seealso:: - - `ImageMagick Geometry Specifications`__ - Cropping and resizing geometry for the ``transform`` method are - specified according to ImageMagick's geometry string format. - The ImageMagick documentation provides more information about - geometry strings. - - __ http://www.imagemagick.org/script/command-line-processing.php#geometry - - .. versionadded:: 0.2.2 - .. versionchanged:: 0.5.0 - Will call :meth:`crop()` followed by :meth:`resize()` in the event - that :c:func:`MagickTransformImage` is not available. - .. deprecated:: 0.6.0 - Use :meth:`crop()` and :meth:`resize()` instead. - """ # noqa - # Check that the values given are the correct types. ctypes will do - # this automatically, but we can make the error message more friendly - # here. - assertions.assert_string(crop=crop, resize=resize) - # Also verify that only ASCII characters are included - try: - crop = crop.encode('ascii') - except UnicodeEncodeError: - raise ValueError('crop must only contain ascii-encodable ' + - 'characters.') - try: - resize = resize.encode('ascii') - except UnicodeEncodeError: - raise ValueError('resize must only contain ascii-encodable ' + - 'characters.') - if not library.MagickTransformImage: # pragma: no cover - # Method removed from ImageMagick-7. - if crop: - x = ctypes.c_ssize_t(0) - y = ctypes.c_ssize_t(0) - width = ctypes.c_size_t(self.width) - height = ctypes.c_size_t(self.height) - if b':' in crop: # For "4:3" aspect cropping. - libmagick.ParseMetaGeometry(crop, - ctypes.byref(x), - ctypes.byref(y), - ctypes.byref(width), - ctypes.byref(height)) - else: - libmagick.GetGeometry(crop, - ctypes.byref(x), - ctypes.byref(y), - ctypes.byref(width), - ctypes.byref(height)) - self.crop(top=y.value, - left=x.value, - width=width.value, - height=height.value, - reset_coords=False) - if resize: - x = ctypes.c_ssize_t(0) - y = ctypes.c_ssize_t(0) - width = ctypes.c_size_t(self.width) - height = ctypes.c_size_t(self.height) - libmagick.ParseMetaGeometry(resize, - ctypes.byref(x), - ctypes.byref(y), - ctypes.byref(width), - ctypes.byref(height)) - self.resize(width=width.value, - height=height.value) - # Both `BaseImage.crop` & `BaseImage.resize` will handle - # animation & error handling, so we can stop here. - return True - if self.animation: - new_wand = library.NewMagickWand() - src_wand = library.MagickCoalesceImages(self.wand) - length = library.MagickGetNumberImages(self.wand) - for i in xrange(length): - library.MagickSetIteratorIndex(src_wand, i) - tmp_wand = library.MagickTransformImage(src_wand, - crop, - resize) - library.MagickAddImage(new_wand, tmp_wand) - if bool(tmp_wand): - library.DestroyMagickWand(tmp_wand) - if bool(src_wand): - library.DestroyMagickWand(src_wand) - self.reset_sequence() - else: - new_wand = library.MagickTransformImage(self.wand, crop, resize) - if new_wand: - self.wand = new_wand - return bool(new_wand) - - @manipulative - @trap_exception - def transform_colorspace(self, colorspace_type): - """Transform image's colorspace. - - :param colorspace_type: colorspace_type. available value can be found - in the :const:`COLORSPACE_TYPES` - :type colorspace_type: :class:`basestring` - - .. versionadded:: 0.4.2 - - """ - assertions.string_in_list(COLORSPACE_TYPES, - 'wand.image.COLORSPACE_TYPES', - colorspace=colorspace_type) - return library.MagickTransformImageColorspace( - self.wand, - COLORSPACE_TYPES.index(colorspace_type) - ) - - @manipulative - @trap_exception - def transparent_color(self, color, alpha, fuzz=0, invert=False): - """Makes the color ``color`` a transparent color with a tolerance of - fuzz. The ``alpha`` parameter specify the transparency level and the - parameter ``fuzz`` specify the tolerance. - - :param color: The color that should be made transparent on the image, - color object - :type color: :class:`wand.color.Color` - :param alpha: the level of transparency: 1.0 is fully opaque - and 0.0 is fully transparent. - :type alpha: :class:`numbers.Real` - :param fuzz: By default target must match a particular pixel color - exactly. However, in many cases two colors may differ - by a small amount. The fuzz member of image defines how - much tolerance is acceptable to consider two colors as the - same. For example, set fuzz to 10 and the color red at - intensities of 100 and 102 respectively are now - interpreted as the same color for the color. - :type fuzz: :class:`numbers.Real` - :param invert: Boolean to tell to paint the inverse selection. - :type invert: :class:`bool` - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.6.3 - - Parameter ``fuzz`` type switched from Integral to Real. - - """ - assertions.assert_real(alpha=alpha, fuzz=fuzz) - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - with color: - r = library.MagickTransparentPaintImage(self.wand, color.resource, - alpha, fuzz, invert) - return r - - @manipulative - def transparentize(self, transparency): - """Makes the image transparent by subtracting some percentage of - the black color channel. The ``transparency`` parameter specifies the - percentage. - - :param transparency: the percentage fade that should be performed on - the image, from 0.0 to 1.0 - :type transparency: :class:`numbers.Real` - - .. versionadded:: 0.2.0 - - """ - if transparency: - t = ctypes.c_double(float(self.quantum_range * - float(transparency))) - if t.value > self.quantum_range or t.value < 0: - raise ValueError('transparency must be a numbers.Real value ' + - 'between 0.0 and 1.0') - # Set the wand to image zero, in case there are multiple images - # in it - library.MagickSetIteratorIndex(self.wand, 0) - # Change the pixel representation of the image - # to RGB with an alpha channel - if MAGICK_VERSION_NUMBER < 0x700: - image_type = 'truecolormatte' - else: # pragma: no cover - image_type = 'truecoloralpha' - library.MagickSetImageType(self.wand, - IMAGE_TYPES.index(image_type)) - # Perform the black channel subtraction - self.evaluate(operator='subtract', - value=t.value, - channel='opacity') - self.raise_exception() - - @manipulative - @trap_exception - def transpose(self): - """Creates a vertical mirror image by reflecting the pixels around - the central x-axis while rotating them 90-degrees. - - .. versionadded:: 0.4.1 - """ - return library.MagickTransposeImage(self.wand) - - @manipulative - @trap_exception - def transverse(self): - """Creates a horizontal mirror image by reflecting the pixels around - the central y-axis while rotating them 270-degrees. - - .. versionadded:: 0.4.1 - """ - return library.MagickTransverseImage(self.wand) - - @manipulative - @trap_exception - def trim(self, color=None, fuzz=0.0, reset_coords=False, - percent_background=None, background_color=None): - """Remove solid border from image. Uses top left pixel as a guide - by default, or you can also specify the ``color`` to remove. - - :param color: the border color to remove. - if it's omitted top left pixel is used by default - :type color: :class:`~wand.color.Color` - :param fuzz: Defines how much tolerance is acceptable to consider - two colors as the same. Value can be between ``0.0``, - and :attr:`quantum_range`. - :type fuzz: :class:`numbers.Real` - :param reset_coords: Reset coordinates after trimming image. Default - ``False``. - :type reset_coords: :class:`bool` - :param percent_background: Sets how aggressive the trim operation will - be. A value of `0.0` will trim to the - minimal bounding box of all matching color, - and `1.0` to the most outer edge. - :type percent_background: :class:`numbers.Real` - :param background_color: Local alias to :attr:`background_color`, - and has the same effect as defining ``color`` - parameter -- but much faster. - - - .. versionadded:: 0.2.1 - - .. versionchanged:: 0.3.0 - Optional ``color`` and ``fuzz`` parameters. - - .. versionchanged:: 0.5.2 - The ``color`` parameter may accept color-compliant strings. - - .. versionchanged:: 0.6.0 - Optional ``reset_coords`` parameter added. - - .. versionchanged:: 0.6.4 - Optional ``percent_background`` & ``background_color`` parameters - have been added. - """ - use_border = background_color is None - if use_border: - if color is None: - color = self[0, 0] - elif isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - with color: - self.border(color, 1, 1, compose='copy') - assertions.assert_real(fuzz=fuzz) - assertions.assert_bool(reset_coords=reset_coords) - if percent_background is not None: - assertions.assert_real(percent_background=percent_background) - percent_background = max(min(percent_background, 1.0), 0.0) * 100.0 - str_pb = '{0:g}%'.format(percent_background) - library.MagickSetImageArtifact(self.wand, - binary('trim:percent-background'), - binary(str_pb)) - if not use_border: - if isinstance(background_color, string_type): - background_color = Color(background_color) - assertions.assert_color(background_color=background_color) - bc_key = 'trim:background-color' - bc_val = background_color.string - library.MagickSetImageArtifact(self.wand, - binary(bc_key), - binary(bc_val)) - r = library.MagickTrimImage(self.wand, fuzz) - if reset_coords: - self.reset_coords() - elif use_border: - # Re-calculate page coordinates as we added a 1x1 border before - # applying the trim. - adjusted_coords = list(self.page) - # Width & height are unsigned. - adjusted_coords[0] = max(adjusted_coords[0] - 2, 0) - adjusted_coords[1] = max(adjusted_coords[1] - 2, 0) - # X & Y are signed. It's common for page offsets to be negative. - adjusted_coords[2] -= 1 - adjusted_coords[3] -= 1 - self.page = adjusted_coords - return r - - @manipulative - @trap_exception - def unique_colors(self): - """Discards all duplicate pixels, and rebuilds the image - as a single row. - - .. versionadded:: 0.5.0 - """ - return library.MagickUniqueImageColors(self.wand) - - @manipulative - @trap_exception - def unsharp_mask(self, radius=0.0, sigma=1.0, amount=1.0, threshold=0.0, - channel=None): - """Sharpens the image using unsharp mask filter. We convolve the image - with a Gaussian operator of the given ``radius`` and standard deviation - (``sigma``). For reasonable results, ``radius`` should be larger than - ``sigma``. Use a radius of 0 and :meth:`unsharp_mask()` selects - a suitable radius for you. - - :see: Example of :ref:`unsharp_mask`. - - :param radius: the radius of the Gaussian, in pixels, - not counting the center pixel - :type radius: :class:`numbers.Real` - :param sigma: the standard deviation of the Gaussian, in pixels - :type sigma: :class:`numbers.Real` - :param amount: the percentage of the difference between the original - and the blur image that is added back into the original - :type amount: :class:`numbers.Real` - :param threshold: the threshold in pixels needed to apply - the difference amount. - :type threshold: :class:`numbers.Real` - :param channel: Optional color channel to target. See - :const:`CHANNELS` - :type channel: :class:`basestring` - - .. versionadded:: 0.3.4 - - .. versionchanged:: 0.5.5 - Added optional ``channel`` argument. - - .. versionchanged:: 0.5.7 - Added default values to match CLI behavior. - """ - assertions.assert_real(radius=radius, sigma=sigma, - amount=amount, threshold=threshold) - if channel is None: - r = library.MagickUnsharpMaskImage(self.wand, radius, sigma, - amount, threshold) - else: - channel_ch = self._channel_to_mask(channel) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickUnsharpMaskImageChannel( - self.wand, channel_ch, radius, sigma, amount, threshold - ) - else: # pragma: no cover - mask = library.MagickSetImageChannelMask(self.wand, channel_ch) - r = library.MagickUnsharpMaskImage(self.wand, radius, sigma, - amount, threshold) - library.MagickSetImageChannelMask(self.wand, mask) - return r - - @manipulative - @trap_exception - def vignette(self, radius=0.0, sigma=0.0, x=0, y=0): - """Creates a soft vignette style effect on the image. - - :see: Example of :ref:`vignette`. - - :param radius: the radius of the Gaussian blur effect. - :type radius: :class:`numbers.Real` - :param sigma: the standard deviation of the Gaussian effect. - :type sigma: :class:`numbers.Real` - :param x: Number of pixels to offset inward from the top & bottom of - the image before drawing effect. - :type x: :class:`numbers.Integral` - :param y: Number of pixels to offset inward from the left & right of - the image before drawing effect. - :type y: :class:`numbers.Integral` - - .. versionadded:: 0.5.2 - """ - assertions.assert_real(radius=radius, sigma=sigma) - return library.MagickVignetteImage(self.wand, radius, sigma, x, y) - - @manipulative - def watermark(self, image, transparency=0.0, left=0, top=0): - """Transparentized the supplied ``image`` and places it over the - current image, with the top left corner of ``image`` at coordinates - ``left``, ``top`` of the current image. The dimensions of the - current image are not changed. - - :param image: the image placed over the current image - :type image: :class:`wand.image.Image` - :param transparency: the percentage fade that should be performed on - the image, from 0.0 to 1.0 - :type transparency: :class:`numbers.Real` - :param left: the x-coordinate where `image` will be placed - :type left: :class:`numbers.Integral` - :param top: the y-coordinate where `image` will be placed - :type top: :class:`numbers.Integral` - - .. versionadded:: 0.2.0 - - """ - with image.clone() as watermark_image: - watermark_image.transparentize(transparency) - watermark_image.clamp() - self.composite(watermark_image, left=left, top=top) - self.raise_exception() - - @manipulative - @trap_exception - def wave(self, amplitude=0.0, wave_length=0.0, method='undefined'): - """Creates a ripple effect within the image. - - :see: Example of :ref:`wave`. - - :param amplitude: height of wave form. - :type amplitude: :class:`numbers.Real` - :param wave_length: width of wave form. - :type wave_length: :class:`numbers.Real` - :param method: pixel interpolation method. Only available with - ImageMagick-7. See :const:`PIXEL_INTERPOLATE_METHODS` - :type method: :class:`basestring` - - .. versionadded:: 0.5.2 - """ - assertions.assert_real(amplitude=amplitude, wave_length=wave_length) - assertions.string_in_list(PIXEL_INTERPOLATE_METHODS, - 'wand.image.PIXEL_INTERPOLATE_METHODS', - method=method) - if MAGICK_VERSION_NUMBER < 0x700: - r = library.MagickWaveImage(self.wand, amplitude, wave_length) - else: # pragma: no cover - method_idx = PIXEL_INTERPOLATE_METHODS.index(method) - r = library.MagickWaveImage(self.wand, amplitude, wave_length, - method_idx) - return r - - @manipulative - @trap_exception - def wavelet_denoise(self, threshold=0.0, softness=0.0): - """Removes noise by applying a `wavelet transform`_. - - .. _`wavelet transform`: - https://en.wikipedia.org/wiki/Wavelet_transform - - .. warning:: - - This class method is only available with ImageMagick 7.0.8-41, or - greater. - - :see: Example of :ref:`wavelet_denoise`. - - :param threshold: Smoothing limit. - :type threshold: :class:`numbers.Real` - :param softness: Attenuate of the smoothing threshold. - :type softness: :class:`numbers.Real` - :raises WandLibraryVersionError: If system's version of ImageMagick - does not support this method. - - .. versionadded:: 0.5.5 - """ - if library.MagickWaveletDenoiseImage is None: - msg = 'Method requires ImageMagick version 7.0.8-41 or greater.' - raise WandLibraryVersionError(msg) - assertions.assert_real(threshold=threshold, softness=softness) - if 0.0 < threshold <= 1.0: - threshold *= self.quantum_range - if 0.0 < softness <= 1.0: - softness *= self.quantum_range - return library.MagickWaveletDenoiseImage(self.wand, threshold, - softness) - - @manipulative - @trap_exception - def white_balance(self): - """Uses LAB colorspace to apply a white balance to the image. - - .. note:: - - Requires ImageMagick-7.0.10-37 or later. - - .. versionadded:: 0.6.4 - """ - msg = 'Requires ImageMagick-7.0.10-37, or later.' - if MAGICK_VERSION_NUMBER < 0x70A: - raise WandLibraryVersionError(msg) - elif library.MagickWhiteBalanceImage is None: - raise WandLibraryVersionError(msg) - return library.MagickWhiteBalanceImage(self.wand) - - @manipulative - @trap_exception - def white_threshold(self, threshold): - """Forces all pixels above a given color as white. Leaves pixels - below threshold unaltered. - - :param threshold: Color to be referenced as a threshold. - :type threshold: :class:`Color` - - .. versionadded:: 0.5.2 - """ - if isinstance(threshold, string_type): - threshold = Color(threshold) - assertions.assert_color(threshold=threshold) - with threshold: - r = library.MagickWhiteThresholdImage(self.wand, - threshold.resource) - return r - - @trap_exception - def write_mask(self, clip_mask=None): - """Sets the write mask which prevents pixel-value updates to the image. - Call this method with a ``None`` argument to clear any previously set - masks. - - .. warning:: - This method is only available with ImageMagick-7. - - :param clip_mask: Image to reference as blend mask. - :type clip_mask: :class:`BaseImage` - - .. versionadded:: 0.5.7 - """ - r = False - WritePixelMask = 0x000002 - if library.MagickSetImageMask is None: - raise WandLibraryVersionError('Method requires ImageMagick-7.') - else: # pragma: no cover - if clip_mask is None: - r = library.MagickSetImageMask(self.wand, WritePixelMask, None) - elif isinstance(clip_mask, BaseImage): - r = library.MagickSetImageMask(self.wand, WritePixelMask, - clip_mask.wand) - return r - - -class Image(BaseImage): - """An image object. - - :param image: makes an exact copy of the ``image`` - :type image: :class:`Image` - :param blob: opens an image of the ``blob`` byte array - :type blob: :class:`bytes` - :param file: opens an image of the ``file`` object - :type file: file object - :param filename: opens an image of the ``filename`` string. Additional - :ref:`read_mods` are supported. - :type filename: :class:`basestring` - :param format: forces filename to buffer. ``format`` to help - ImageMagick detect the file format. Used only in - ``blob`` or ``file`` cases - :type format: :class:`basestring` - :param width: the width of new blank image or an image loaded from raw - data. - :type width: :class:`numbers.Integral` - :param height: the height of new blank image or an image loaded from - raw data. - :type height: :class:`numbers.Integral` - :param depth: the depth used when loading raw data. - :type depth: :class:`numbers.Integral` - :param background: an optional background color. - default is transparent - :type background: :class:`wand.color.Color` - :param resolution: set a resolution value (dpi), - useful for vectorial formats (like pdf) - :type resolution: :class:`collections.abc.Sequence`, - :Class:`numbers.Integral` - :param colorspace: sets the stack's default colorspace value before - reading any images. - See :const:`COLORSPACE_TYPES`. - :type colorspace: :class:`basestring` - :param units: paired with ``resolution`` for defining an image's pixel - density. See :const:`UNIT_TYPES`. - :type units: :class:`basestring` - - .. versionadded:: 0.1.5 - The ``file`` parameter. - - .. versionadded:: 0.1.1 - The ``blob`` parameter. - - .. versionadded:: 0.2.1 - The ``format`` parameter. - - .. versionadded:: 0.2.2 - The ``width``, ``height``, ``background`` parameters. - - .. versionadded:: 0.3.0 - The ``resolution`` parameter. - - .. versionadded:: 0.4.2 - The ``depth`` parameter. - - .. versionchanged:: 0.4.2 - The ``depth``, ``width`` and ``height`` parameters can be used - with the ``filename``, ``file`` and ``blob`` parameters to load - raw pixel data. - - .. versionadded:: 0.5.0 - The ``pseudo`` parameter. - - .. versionchanged:: 0.5.4 - Read constructor no longer sets "transparent" background by default. - Use the ``background`` paramater to specify canvas color when reading - in image. - - .. versionchanged:: 0.5.7 - Added the ``colorspace`` & ``units`` parameter. - - .. versionchanged:: 0.6.3 - Added ``sampling_factors`` parameter for working with YUV streams. - - .. describe:: [left:right, top:bottom] - - Crops the image by its ``left``, ``right``, ``top`` and ``bottom``, - and then returns the cropped one. :: - - with img[100:200, 150:300] as cropped: - # manipulated the cropped image - pass - - Like other subscriptable objects, default is 0 or its width/height:: - - img[:, :] #--> just clone - img[:100, 200:] #--> equivalent to img[0:100, 200:img.height] - - Negative integers count from the end (width/height):: - - img[-70:-50, -20:-10] - #--> equivalent to img[width-70:width-50, height-20:height-10] - - :returns: the cropped image - :rtype: :class:`Image` - - .. versionadded:: 0.1.2 - - """ - - #: (:class:`ArtifactTree`) A dict mapping to image artifacts. - #: Similar to :attr:`metadata`, but used to alter behavior of various - #: internal operations. - #: - #: .. versionadded:: 0.5.0 - artifacts = None - - #: (:class:`ChannelImageDict`) The mapping of separated channels - #: from the image. :: - #: - #: with image.channel_images['red'] as red_image: - #: display(red_image) - channel_images = None - - #: (:class:`ChannelDepthDict`) The mapping of channels to their depth. - #: Read only. - #: - #: .. versionadded:: 0.3.0 - channel_depths = None - - #: (:class:`Metadata`) The metadata mapping of the image. Read only. - #: - #: .. versionadded:: 0.3.0 - metadata = None - - #: (:class:`ProfileDict`) The mapping of image profiles. - #: - #: .. versionadded:: 0.5.1 - profiles = None - - def __init__(self, image=None, blob=None, file=None, filename=None, - pseudo=None, background=None, colorspace=None, depth=None, - extract=None, format=None, height=None, interlace=None, - resolution=None, sampling_factors=None, units=None, - width=None): - new_args = width, height, background, depth - open_args = blob, file, filename - if any(a is not None for a in new_args) and image is not None: - raise TypeError("blank image parameters can't be used with image " - 'parameter') - if sum(a is not None for a in open_args + (image,)) > 1: - raise TypeError(', '.join(open_args) + - ' and image parameters are exclusive each other; ' - 'use only one at once') - with self.allocate(): - if image is None: - wand = library.NewMagickWand() - super(Image, self).__init__(wand) - if image is not None: - if not isinstance(image, BaseImage): - raise TypeError('image must be a wand.image.Image ' - 'instance, not ' + repr(image)) - wand = library.CloneMagickWand(image.wand) - super(Image, self).__init__(wand) - elif any(a is not None for a in open_args): - self._preamble_read( - background=background, colorspace=colorspace, depth=depth, - extract=extract, format=format, height=height, - interlace=interlace, resolution=resolution, - sampling_factors=sampling_factors, width=width - ) - if file is not None: - self.read(file=file) - elif blob is not None: - self.read(blob=blob) - elif filename is not None: - self.read(filename=filename) - # clear the wand format, otherwise any subsequent call to - # MagickGetImageBlob will silently change the image to this - # format again. - library.MagickSetFormat(self.wand, binary("")) - elif width is not None and height is not None: - if pseudo is None: - self.blank(width, height, background) - else: - self.pseudo(width, height, pseudo) - if depth: - r = library.MagickSetImageDepth(self.wand, depth) - if not r: - raise self.raise_exception() - if units is not None: - self.units = units - self.metadata = Metadata(self) - self.artifacts = ArtifactTree(self) - from .sequence import Sequence - self.sequence = Sequence(self) - self.profiles = ProfileDict(self) - self.raise_exception() - - def __repr__(self): - return super(Image, self).__repr__( - extra_format=' {self.format!r} ({self.width}x{self.height})' - ) - - def _preamble_read(self, background=None, colorspace=None, depth=None, - extract=None, format=None, height=None, interlace=None, - resolution=None, sampling_factors=None, units=None, - width=None): - """Set-up MagickWand properties before reading an image file. The - properties are unique to the image decoder. - - :param background: Defines the default background color. - :type background: :class:`Color`, :class:`basestring` - :param colorspace: Defines what colorspace the decoder should operate - in. See :const:`COLORSPACE_TYPES`. - :type colorspace: :class:`basestring` - :param depth: Bits per color sample. - :type depth: :class:`numbers.Integral` - :param extract: Only decode a sub-region of the image. - :type extract: :class:`basestring` - :param format: Defines the decoder image format. - :type format: :class:`basestring` - :param height: Defines how high a blank canvas should be. Only used if - ``width`` is also defined. - :type height: :class:`numbers.Integral` - :param interlace: Defines the interlacing scheme for raw data streams. - See :const:`INTERLACE_TYPES`. - :type interlace: :class:`basestring` - :param resolution: Defines the pixel density of a scalable formats. - PDF & SVG as examples. - :type resolution: :class:`collections.abc.Sequence`, - :class:`numbers.Integral` - :param sampling_factors: Defines how a YUV might be upsampled. - :type sampling_factors: :class:`collections.abc.Sequence`, - :class:`basestring` - :param units: Unused. - :type units: :class:`numbers.Integral` - :param width: Defines how wide a blank canvas should be. Only used if - ``height`` is also defined. - :type width: :class:`numbers.Intragal` - - .. versionadded:: 0.6.3 - """ - if background: - if isinstance(background, string_type): - background = Color(background) - assertions.assert_color(background=background) - with background: - library.MagickSetBackgroundColor(self.wand, - background.resource) - if colorspace is not None: - assertions.string_in_list( - COLORSPACE_TYPES, - 'wand.image.COLORSPACE_TYPES', - colorspace=colorspace - ) - colorspace_idx = COLORSPACE_TYPES.index(colorspace) - library.MagickSetColorspace(self.wand, colorspace_idx) - if depth is not None: - assertions.assert_counting_number(depth=depth) - library.MagickSetDepth(self.wand, depth) - if extract is not None: - assertions.assert_string(extract=extract) - library.MagickSetExtract(self.wand, binary(extract)) - if format is not None: - assertions.assert_string(format=format) - library.MagickSetFormat(self.wand, binary(format)) - library.MagickSetFilename(self.wand, b'buffer.' + binary(format)) - if interlace is not None: - assertions.string_in_list( - INTERLACE_TYPES, - 'wand.image.INTERLACE_TYPES', - interlace=interlace - ) - c_interlace = INTERLACE_TYPES.index(interlace) - library.MagickSetInterlaceScheme(self.wand, c_interlace) - if resolution is not None: - if (isinstance(resolution, abc.Sequence) and - len(resolution) == 2): - library.MagickSetResolution(self.wand, *resolution) - elif isinstance(resolution, numbers.Real): - library.MagickSetResolution(self.wand, resolution, resolution) - else: - raise TypeError('resolution must be a (x, y) pair or an ' - 'real number of the same x/y') - if sampling_factors is not None: - self.sampling_factors = sampling_factors - if width is not None and height is not None: - assertions.assert_counting_number(width=width, height=height) - library.MagickSetSize(self.wand, width, height) - - def _repr_png_(self): - with self.convert('png') as cloned: - return cloned.make_blob() - - @classmethod - def from_array(cls, array, channel_map=None, storage=None): - """Create an image instance from a :mod:`numpy` array, or any other - datatype that implements `__array_interface__`__ protocol. - - .. code:: - - import numpy - from wand.image import Image - - matrix = numpy.random.rand(100, 100, 3) - with Image.from_array(matrix) as img: - img.save(filename='noise.png') - - Use the optional ``channel_map`` & ``storage`` arguments to specify - the order of color channels & data size. If ``channel_map`` is omitted, - this method will will guess ``"RGB"``, or ``"CMYK"`` based on - array shape. If ``storage`` is omitted, this method will reference the - array's ``typestr`` value, and raise a :class:`ValueError` if - storage-type can not be mapped. - - Float values must be normalized between `0.0` and `1.0`, and signed - integers should be converted to unsigned values between `0` and - max value of type. - - Instances of :class:`Image` can also be exported to numpy arrays:: - - with Image(filename='rose:') as img: - matrix = numpy.array(img) - - __ https://docs.scipy.org/doc/numpy/reference/arrays.interface.html - - :param array: Numpy array of pixel values. - :type array: :class:`numpy.array` - :param channel_map: Color channel layout. - :type channel_map: :class:`basestring` - :param storage: Datatype per pixel part. - :type storage: :class:`basestring` - :returns: New instance of an image. - :rtype: :class:`~wand.image.Image` - - .. versionadded:: 0.5.3 - .. versionchanged:: 0.6.0 - Input ``array`` now expects the :attr:`shape` property to be defined - as ```( 'height', 'width', 'channels' )```. - """ - arr_itr = array.__array_interface__ - typestr = arr_itr['typestr'] # Required by interface. - shape = arr_itr['shape'] # Required by interface. - if storage is None: - # Attempt to guess storage - storage_map = dict(u1='char', i1='char', - u2='short', i2='short', - u4='integer', i4='integer', - u8='long', i8='integer', - f4='float', f8='double') - for token in storage_map: - if token in typestr: - storage = storage_map[token] - break - if storage is None: - raise ValueError('Unable to determine storage type.') - if channel_map is None: - # Attempt to guess channel map - if len(shape) == 3: - if shape[2] < 5: - channel_map = 'RGBA'[0:shape[2]] - else: - channel_map = 'CMYKA'[0:shape[2]] - else: - channel_map = 'R' - strides = arr_itr.get('strides', None) - if hasattr(array, 'ctypes') and strides is None: - data_ptr = array.ctypes.data_as(ctypes.c_void_p) - elif hasattr(array, 'tobytes'): - data_ptr = array.tobytes() - elif hasattr(array, 'tostring'): - data_ptr = array.tostring() - else: - data_ptr, _ = arr_itr.get('data') - storage_idx = STORAGE_TYPES.index(storage) - height, width = shape[:2] - genesis() - wand = library.NewMagickWand() - instance = cls(BaseImage(wand)) - r = library.MagickConstituteImage(instance.wand, - width, - height, - binary(channel_map), - storage_idx, - data_ptr) - if not r: - instance.raise_exception(cls) - return instance - - @classmethod - def ping(cls, file=None, filename=None, blob=None, **kwargs): - """Ping image header into Image() object, but without any pixel data. - This is useful for inspecting image meta-data without decoding the - whole image. - - :param blob: reads an image from the ``blob`` byte array - :type blob: :class:`bytes` - :param file: reads an image from the ``file`` object - :type file: file object - :param filename: reads an image from the ``filename`` string - :type filename: :class:`basestring` - :param resolution: set a resolution value (DPI), - useful for vector formats (like PDF) - :type resolution: :class:`collections.abc.Sequence`, - :class:`numbers.Integral` - :param format: suggest image file format when reading from a ``blob``, - or ``file`` property. - :type format: :class:`basestring` - - .. versionadded:: 0.5.6 - - """ - r = None - instance = cls() - instance._preamble_read(**kwargs) - if file is not None: - if (isinstance(file, file_types) and - hasattr(libc, 'fdopen') and hasattr(file, 'mode')): - fd = libc.fdopen(file.fileno(), file.mode) - r = library.MagickPingImageFile(instance.wand, fd) - elif not callable(getattr(file, 'read', None)): - raise TypeError('file must be a readable file object' - ', but the given object does not ' - 'have read() method') - else: - blob = file.read() - file = None - if blob is not None: - if not isinstance(blob, abc.Iterable): - raise TypeError('blob must be iterable, not ' + - repr(blob)) - if not isinstance(blob, binary_type): - blob = b''.join(blob) - r = library.MagickPingImageBlob(instance.wand, blob, len(blob)) - elif filename is not None: - filename = encode_filename(filename) - r = library.MagickPingImage(instance.wand, filename) - if not r: - instance.raise_exception() - msg = ('MagickPingImage returns false, but did raise ImageMagick ' - 'exception. This can occur when a delegate is missing, or ' - 'returns EXIT_SUCCESS without generating a raster.') - raise WandRuntimeError(msg) - else: - units = kwargs.get('units') - if units is not None: - instance.units = units - instance.metadata = Metadata(instance) - instance.artifacts = ArtifactTree(instance) - from .sequence import Sequence - instance.sequence = Sequence(instance) - instance.profiles = ProfileDict(instance) - return instance - - @classmethod - def stereogram(cls, left, right): - """Create a new stereogram image from two existing images. - - :see: Example of :ref:`stereogram`. - - :param left: Left-eye image. - :type left: :class:`wand.image.Image` - :param right: Right-eye image. - :type right: :class:`wand.image.Image` - - .. versionadded:: 0.5.4 - """ - if not isinstance(left, BaseImage): - raise TypeError('Left image must be in instance of ' - 'wand.image.Image, not ' + repr(left)) - if not isinstance(right, BaseImage): - raise TypeError('Right image must be in instance of ' - 'wand.image.Image, not ' + repr(right)) - wand = library.MagickStereoImage(left.wand, right.wand) - if not wand: # pragma: no cover - left.raise_exception() - return cls(BaseImage(wand)) - - @property - def animation(self): - is_gif = self.mimetype in ('image/gif', 'image/x-gif') - frames = library.MagickGetNumberImages(self.wand) - return is_gif and frames > 1 - - @property - def mimetype(self): - """(:class:`basestring`) The MIME type of the image - e.g. ``'image/jpeg'``, ``'image/png'``. - - .. versionadded:: 0.1.7 - - """ - mtype = None - rp = libmagick.MagickToMime(binary(self.format)) - if rp: - mtype = text(ctypes.string_at(rp)) - rp = libmagick.DestroyString(rp) - return mtype - - def blank(self, width, height, background=None): - """Creates blank image. - - :param width: the width of new blank image. - :type width: :class:`numbers.Integral` - :param height: the height of new blank image. - :type height: :class:`numbers.Integral` - :param background: an optional background color. - default is transparent - :type background: :class:`wand.color.Color` - :returns: blank image - :rtype: :class:`Image` - - .. versionadded:: 0.3.0 - - """ - assertions.assert_counting_number(width=width, height=height) - if background is None: - background = Color('transparent') - elif isinstance(background, string_type): - background = Color(background) - assertions.assert_color(background=background) - with background: - r = library.MagickNewImage(self.wand, width, height, - background.resource) - if not r: - self.raise_exception() - return self - - def clear(self): - """Clears resources associated with the image, leaving the image blank, - and ready to be used with new image. - - .. versionadded:: 0.3.0 - - """ - library.ClearMagickWand(self.wand) - - def close(self): - """Closes the image explicitly. If you use the image object in - :keyword:`with` statement, it was called implicitly so don't have to - call it. - - .. note:: - - It has the same functionality of :attr:`destroy()` method. - - """ - self.destroy() - - def compare_layers(self, method): - """Generates new images showing the delta pixels between - layers. Similar pixels are converted to transparent. - Useful for debugging complex animations. :: - - with img.compare_layers('compareany') as delta: - delta.save(filename='framediff_%02d.png') - - .. note:: - - May not work as expected if animations are already - optimized. - - :param method: Can be ``'compareany'``, - ``'compareclear'``, or ``'compareoverlay'`` - :type method: :class:`basestring` - :returns: new image stack. - :rtype: :class:`Image` - - .. versionadded:: 0.5.0 - """ - if not isinstance(method, string_type): - raise TypeError('method must be a string from IMAGE_LAYER_METHOD, ' - 'not ' + repr(method)) - if method not in ('compareany', 'compareclear', 'compareoverlay'): - raise ValueError('method can only be \'compareany\', ' - '\'compareclear\', or \'compareoverlay\'') - r = None - m = IMAGE_LAYER_METHOD.index(method) - if MAGICK_VERSION_NUMBER >= 0x700: # pragma: no cover - r = library.MagickCompareImagesLayers(self.wand, m) - elif library.MagickCompareImageLayers: - r = library.MagickCompareImageLayers(self.wand, m) - elif library.MagickCompareImagesLayers: # pragma: no cover - r = library.MagickCompareImagesLayers(self.wand, m) - else: - raise AttributeError('MagickCompareImageLayers method ' - 'not available on system.') - if not r: - self.raise_exception() - return Image(image=BaseImage(r)) - - def convert(self, format): - """Converts the image format with the original image maintained. - It returns a converted image instance which is new. :: - - with img.convert('png') as converted: - converted.save(filename='converted.png') - - :param format: image format to convert to - :type format: :class:`basestring` - :returns: a converted image - :rtype: :class:`Image` - :raises ValueError: when the given ``format`` is unsupported - - .. versionadded:: 0.1.6 - - .. versionchanged:: 0.6.11 - Call :c:func:`MagickSetFormat` method after - :c:func:`MagickSetImageFormat`. This will ensure image info, magick, - and filename properties are aligned. - """ - cloned = self.clone() - cloned.format = format - library.MagickSetFormat(cloned.wand, - binary(format.strip().upper())) - return cloned - - def data_url(self): - """Generate a base64 `data-url`_ string from the loaded image. - Useful for converting small graphics into ASCII strings for HTML/CSS - web development. - - .. _data-url: https://en.wikipedia.org/wiki/Data_URI_scheme - - :returns: a data-url formated string. - :rtype: :class:`basestring` - - .. versionadded:: 0.6.3 - """ - from base64 import b64encode - mime_type = self.mimetype - base_bytes = b64encode(self.make_blob()) - return "data:{0};base64,{1}".format(mime_type, text(base_bytes)) - - @trap_exception - def image_add(self, image): - """Copies a given image on to the image stack. By default, the added - image will be append at the end of the stack, or immediately after - the current image iterator defined by :meth:`~BaseImage.iterator_set`. - Use :meth:`~BaseImage.iterator_reset` before calling this method to - insert the new image before existing images on the stack. - - :param image: raster to add. - :type image: :class:`Image` - - .. versionadded:: 0.6.7 - """ - if not isinstance(image, Image): - raise TypeError('image must be instance of wand.image.Image') - return library.MagickAddImage(self.wand, image.wand) - - def image_get(self): - """Generate & return a clone of a single image at the current - image-stack index. - - .. versionadded:: 0.6.7 - """ - r = library.MagickGetImage(self.wand) - if not r: - self.raise_exception() - return None # noqa - Safety if exception isn't thrown. - return Image(BaseImage(r)) - - @trap_exception - def image_remove(self): - """Remove an image from the image-stack at the current index. - - .. versionadded:: 0.6.7 - """ - return library.MagickRemoveImage(self.wand) - - @trap_exception - def image_set(self, image): - """Overwrite current image on the image-stack with given image. - - :param image: Wand instance of images to write to stack. - :type image: :class:`wand.image.Image` - - .. versionadded:: 0.6.7 - """ - if not isinstance(image, Image): - raise TypeError('image must be an instance of wand.image.Image,', - ' not ' + repr(image)) - return library.MagickSetImage(self.wand, image.wand) - - @trap_exception - def image_swap(self, i, j): - """Swap two images on the image-stack. - - :param i: image index to replace with ``j`` - :type i: :class:`numbers.Integral` - :param j: image index to replace with ``i`` - :type j: :class:`numbers.Integral` - - .. versionadded:: 0.6.7 - """ - assertions.assert_integer(i=i) - assertions.assert_integer(j=j) - op = self.iterator_get() - self.iterator_set(i) - with self.image_get() as a: - self.iterator_set(j) - with self.image_get() as b: - self.image_set(a) - self.iterator_set(i) - self.image_set(b) - self.iterator_set(op) - - def make_blob(self, format=None): - """Makes the binary string of the image. - - :param format: the image format to write e.g. ``'png'``, ``'jpeg'``. - it is omittable - :type format: :class:`basestring` - :returns: a blob (bytes) string - :rtype: :class:`bytes` - :raises ValueError: when ``format`` is invalid - - .. versionchanged:: 0.1.6 - Removed a side effect that changes the image :attr:`format` - silently. - - .. versionadded:: 0.1.5 - The ``format`` parameter became optional. - - .. versionadded:: 0.1.1 - - """ - if format is not None: - with self.convert(format) as converted: - return converted.make_blob() - library.MagickResetIterator(self.wand) - length = ctypes.c_size_t() - blob_p = None - if library.MagickGetNumberImages(self.wand) > 1: - blob_p = library.MagickGetImagesBlob(self.wand, - ctypes.byref(length)) - else: - blob_p = library.MagickGetImageBlob(self.wand, - ctypes.byref(length)) - if blob_p and length.value: - blob = ctypes.string_at(blob_p, length.value) - blob_p = library.MagickRelinquishMemory(blob_p) - return blob - else: # pragma: no cover - self.raise_exception() - - @trap_exception - def montage(self, font=None, tile=None, thumbnail=None, mode='unframe', - frame=None): - """Generates a new image containing thumbnails if each previous image - read. :: - - with Image() as img: - for file_path in ['first.png', 'second.png', 'third.png']: - with Image(filename=file_path) as item: - img.options['label'] = file_path - img.image_add(item) - style = Font('monospace', 24, 'green') - img.montage(font=style, tile='3x1', thumbnail='15x15') - img.save(filename='montage.png') - - :param font: Define font style to use when labeling each thumbnail. - Thumbnail labeling will only be rendered if ``'label'`` - value in :attr:`options` dict is defined. - :type font: :class:`~wand.font.Font` - :param tile: The number of thunbnails per rows & column on a page. - Example: ``"6x4"``. - :type tile: :class:`basestring` - :param thumbnail: Preferred image size. Montage will attempt to - generate a thumbnail to match the geometry. This - can also define the border size on each thumbnail. - Example: ``"120x120x+4+3>"``. - :type thumbnail: :class:`basestring` - :param mode: Which effect to render. Options include ``"frame"``, - ``"unframe"``, and ``"concatenate"``. Default ``"frame"``. - :type mode: :class:`basestring` - :param frame: Define ornamental boarder around each thrumbnail. - The color of the frame is defined by the image's matte - color. Example: ``"15x15+3+3"``. - :type frame: :class:`basestring` - - .. versionadded:: 0.6.8 - """ - if font is not None: - if not isinstance(font, Font): - msg = 'font must be an instance of wand.font.Font' - raise TypeError(msg) - else: - font = Font('helvetica', 16, 'black') - if tile is not None: - assertions.assert_string(tile=tile) - tile = binary(tile) - if thumbnail is not None: - assertions.assert_string(thumbnail=thumbnail) - thumbnail = binary(thumbnail) - assertions.in_list(MONTAGE_MODES, - 'wand.image.MONTAGE_MODES', - mode=mode) - mode_idx = MONTAGE_MODES.index(mode) - if frame is not None: - assertions.assert_string(frame=frame) - frame = binary(frame) - ctx_ptr = library.NewDrawingWand() - if font.path: - library.DrawSetFont(ctx_ptr, binary(font.path)) - library.DrawSetFontFamily(ctx_ptr, binary(font.path)) - if font.size: - library.DrawSetFontSize(ctx_ptr, font.size) - if font.color: - with font.color: - library.DrawSetFillColor(ctx_ptr, font.color.resource) - if font.stroke_color: - with font.stroke_color: - library.DrawSetStrokeColor(ctx_ptr, font.stroke_color.resource) - new_wand = library.MagickMontageImage(self.wand, ctx_ptr, tile, - thumbnail, mode_idx, frame) - ctx_ptr = library.DestroyDrawingWand(ctx_ptr) - ok = bool(new_wand) - if ok: - self.wand = new_wand - self.reset_sequence() - return ok - - def pseudo(self, width, height, pseudo='xc:'): - """Creates a new image from ImageMagick's internal protocol coders. - - :param width: Total columns of the new image. - :type width: :class:`numbers.Integral` - :param height: Total rows of the new image. - :type height: :class:`numbers.Integral` - :param pseudo: The protocol & arguments for the pseudo image. - :type pseudo: :class:`basestring` - - .. versionadded:: 0.5.0 - """ - assertions.assert_counting_number(width=width, height=height) - assertions.assert_string(pseudo=pseudo) - r = library.MagickSetSize(self.wand, width, height) - if not r: - self.raise_exception() - r = library.MagickReadImage(self.wand, encode_filename(pseudo)) - if not r: - self.raise_exception() - - def read(self, file=None, filename=None, blob=None, background=None, - colorspace=None, depth=None, extract=None, format=None, - height=None, interlace=None, resolution=None, - sampling_factors=None, units=None, width=None): - """Read new image into Image() object. - - :param blob: reads an image from the ``blob`` byte array - :type blob: :class:`bytes` - :param file: reads an image from the ``file`` object - :type file: file object - :param filename: reads an image from the ``filename`` string. - Additional :ref:`read_mods` are supported. - :type filename: :class:`basestring` - :param background: set default background color. - :type background: :class:`Color`, :class:`basestring` - :param colorspace: set default colorspace. - See :const:`COLORSPACE_TYPES`. - :type colorspace: :class:`basestring` - :param depth: sets bits per color sample. Usually ``8``, or ``16``. - :type depth: :class:`numbers.Integral` - :param format: sets which image decoder to read with. Helpful when - reading ``blob`` data with ambiguous headers. - :type format: :class:`basestring` - :param height: used with ``width`` to define the canvas size. Useful - for reading image streams. - :type height: :class:`numbers.Integral` - :param interlace: Defines the interlacing scheme for raw data streams. - See :const:`INTERLACE_TYPES`. - :type interlace: :class:`basestring` - :param resolution: set a resolution value (DPI), - useful for vectorial formats (like PDF) - :type resolution: :class:`collections.abc.Sequence`, - :class:`numbers.Integral` - :param sampling_factors: set up/down stampling factors for YUV data - stream. Usually ``"4:2:2"`` - :type sampling_factors: :class:`collections.abc.Sequence`, - :class:`basestring` - :param units: used with ``resolution``, can either be - ``'pixelperinch'``, or ``'pixelpercentimeter'``. - :type units: :class:`basestring` - :param width: used with ``height`` to define the canvas size. Useful - for reading image streams. - :type width: :class:`numbers.Integral` - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.5.7 - Added ``units`` parameter. - - .. versionchanged:: 0.6.3 - Added, or documented, optional pre-read parameters: - ``background``, ``colorspace``, ``depth``, ``format``, ``height``, - ``interlace``, ``sampling_factors``, & ``width``. - """ - r = None - # Resolution must be set after image reading. - self._preamble_read( - background=background, colorspace=colorspace, depth=depth, - extract=extract, format=format, height=height, interlace=interlace, - resolution=resolution, sampling_factors=sampling_factors, - width=width - ) - if file is not None: - if (isinstance(file, file_types) and - hasattr(libc, 'fdopen') and hasattr(file, 'mode')): - fd = libc.fdopen(file.fileno(), file.mode) - r = library.MagickReadImageFile(self.wand, fd) - elif not callable(getattr(file, 'read', None)): - raise TypeError('file must be a readable file object' - ', but the given object does not ' - 'have read() method') - else: - blob = file.read() - file = None - if blob is not None: - if not isinstance(blob, abc.Iterable): - raise TypeError('blob must be iterable, not ' + - repr(blob)) - if not isinstance(blob, binary_type): - blob = b''.join(blob) - r = library.MagickReadImageBlob(self.wand, blob, len(blob)) - elif filename is not None: - filename = encode_filename(filename) - r = library.MagickReadImage(self.wand, filename) - if not r: - self.raise_exception() - msg = ('MagickReadImage returns false, but did not raise ' - 'ImageMagick exception. This can occur when a delegate ' - 'is missing, or returns EXIT_SUCCESS without generating a ' - 'raster.') - raise WandRuntimeError(msg) - else: - if units is not None: - self.units = units - - def reset_sequence(self): - """Remove any previously allocated :class:`~wand.sequence.SingleImage` - instances in :attr:`sequence` attribute. - - .. versionadded:: 0.6.0 - """ - for instance in self.sequence.instances: - if hasattr(instance, 'destroy'): - instance.destroy() - self.sequence.instances = [] - - def save(self, file=None, filename=None, adjoin=True): - """Saves the image into the ``file`` or ``filename``. It takes - only one argument at a time. - - :param file: a file object to write to - :type file: file object - :param filename: a filename string to write to - :type filename: :class:`basestring` - :param adjoin: write all images to a single multi-image file. Only - available if file format supports frames, layers, & etc. - :type adjoin: :class:`bool` - - .. versionadded:: 0.1.1 - - .. versionchanged:: 0.1.5 - The ``file`` parameter was added. - - .. versionchanged:: 6.0.0 - The ``adjoin`` parameter was added. - - """ - if file is None and filename is None: - raise TypeError('expected an argument') - elif file is not None and filename is not None: - raise TypeError('expected only one argument; but two passed') - elif file is not None: - if isinstance(file, string_type): - raise TypeError('file must be a writable file object, ' - 'but {0!r} is a string; did you want ' - '.save(filename={0!r})?'.format(file)) - elif isinstance(file, file_types) and hasattr(libc, 'fdopen'): - fd = libc.fdopen(file.fileno(), file.mode) - if library.MagickGetNumberImages(self.wand) > 1: - r = library.MagickWriteImagesFile(self.wand, fd) - else: - r = library.MagickWriteImageFile(self.wand, fd) - libc.fflush(fd) - if not r: - self.raise_exception() - else: - if not callable(getattr(file, 'write', None)): - raise TypeError('file must be a writable file object, ' - 'but it does not have write() method: ' + - repr(file)) - file.write(self.make_blob()) - else: - if not isinstance(filename, string_type): - if not hasattr(filename, '__fspath__'): - raise TypeError('filename must be a string, not ' + - repr(filename)) - filename = encode_filename(filename) - if library.MagickGetNumberImages(self.wand) > 1: - r = library.MagickWriteImages(self.wand, filename, adjoin) - else: - r = library.MagickWriteImage(self.wand, filename) - if not r: - self.raise_exception() - - -class Iterator(Resource, abc.Iterator): - """Row iterator for :class:`Image`. It shouldn't be instantiated - directly; instead, it can be acquired through :class:`Image` instance:: - - assert isinstance(image, wand.image.Image) - iterator = iter(image) - - It doesn't iterate every pixel, but rows. For example:: - - for row in image: - for col in row: - assert isinstance(col, wand.color.Color) - print(col) - - Every row is a :class:`collections.abc.Sequence` which consists of - one or more :class:`wand.color.Color` values. - - :param image: the image to get an iterator - :type image: :class:`Image` - - .. versionadded:: 0.1.3 - - """ - - c_is_resource = library.IsPixelIterator - c_destroy_resource = library.DestroyPixelIterator - c_get_exception = library.PixelGetIteratorException - c_clear_exception = library.PixelClearIteratorException - - def __init__(self, image=None, iterator=None): - if image is not None and iterator is not None: - raise TypeError('it takes only one argument at a time') - with self.allocate(): - if image is not None: - if not isinstance(image, Image): - raise TypeError('expected a wand.image.Image instance, ' - 'not ' + repr(image)) - self.resource = library.NewPixelIterator(image.wand) - self.height = image.height - else: - if not isinstance(iterator, Iterator): - raise TypeError('expected a wand.image.Iterator instance, ' - 'not ' + repr(iterator)) - self.resource = library.ClonePixelIterator(iterator.resource) - self.height = iterator.height - self.raise_exception() - self.cursor = 0 - - def __iter__(self): - return self - - def seek(self, y): - assertions.assert_unsigned_integer(seek=y) - if y > self.height: - raise ValueError('can not be greater than height') - self.cursor = y - if y == 0: - library.PixelSetFirstIteratorRow(self.resource) - else: - if not library.PixelSetIteratorRow(self.resource, y - 1): - self.raise_exception() - - def __next__(self, x=None): - if self.cursor >= self.height: - self.destroy() - raise StopIteration() - self.cursor += 1 - width = ctypes.c_size_t() - pixels = library.PixelGetNextIteratorRow(self.resource, - ctypes.byref(width)) - if x is None: - r_pixels = [None] * width.value - for x in xrange(width.value): - r_pixels[x] = Color.from_pixelwand(pixels[x]) - return r_pixels - return Color.from_pixelwand(pixels[x]) if pixels else None - - next = __next__ # Python 2 compatibility - - def clone(self): - """Clones the same iterator. - - """ - return type(self)(iterator=self) - - -class ImageProperty(object): - """The mixin class to maintain a weak reference to the parent - :class:`Image` object. - - .. versionadded:: 0.3.0 - - """ - - def __init__(self, image): - if not isinstance(image, BaseImage): - raise TypeError('expected a wand.image.BaseImage instance, ' - 'not ' + repr(image)) - self._image = weakref.ref(image) - - @property - def image(self): - """(:class:`Image`) The parent image. - - It ensures that the parent :class:`Image`, which is held in a weak - reference, still exists. Returns the dereferenced :class:`Image` - if it does exist, or raises a :exc:`ClosedImageError` otherwise. - - :exc: `ClosedImageError` when the parent Image has been destroyed - - """ - # Dereference our weakref and check that the parent Image still exists - image = self._image() - if image is not None: - return image - raise ClosedImageError( - 'parent Image of {0!r} has been destroyed'.format(self) - ) - - -class OptionDict(ImageProperty, abc.MutableMapping): - """Free-form mutable mapping of global internal settings. - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.5.0 - Remove key check to :const:`OPTIONS`. Image properties are specific to - vendor, and this library should not attempt to manage the 100+ options - in a whitelist. - """ - - def __iter__(self): - return iter(OPTIONS) - - def __len__(self): - return len(OPTIONS) - - def __getitem__(self, key): - assertions.assert_string(key=key) - opt_str = b'' - opt_p = library.MagickGetOption(self.image.wand, binary(key)) - if opt_p: - opt_str = text(ctypes.string_at(opt_p)) - opt_p = library.MagickRelinquishMemory(opt_p) - else: - raise KeyError(key) - return opt_str - - def __setitem__(self, key, value): - assertions.assert_string(key=key, value=value) - image = self.image - library.MagickSetOption(image.wand, binary(key), binary(value)) - - def __delitem__(self, key): - self[key] = '' - - -class Metadata(ImageProperty, abc.MutableMapping): - """Class that implements dict-like read-only access to image metadata - like EXIF or IPTC headers. Most WRITE encoders will ignore properties - assigned here. - - :param image: an image instance - :type image: :class:`Image` - - .. note:: - - You don't have to use this by yourself. - Use :attr:`Image.metadata` property instead. - - .. versionadded:: 0.3.0 - - """ - - def __init__(self, image): - if not isinstance(image, Image): - raise TypeError('expected a wand.image.Image instance, ' - 'not ' + repr(image)) - super(Metadata, self).__init__(image) - - def __getitem__(self, k): - """ - :param k: Metadata header name string. - :type k: :class:`basestring` - :returns: a header value string - :rtype: :class:`str` - """ - assertions.assert_string(key=k) - image = self.image - value = b'' - vp = library.MagickGetImageProperty(image.wand, binary(k)) - if vp: - value = text(ctypes.string_at(vp)) - vp = library.MagickRelinquishMemory(vp) - else: - raise KeyError(k) - return value - - def __setitem__(self, k, v): - """ - :param k: Metadata header name string. - :type k: :class:`basestring` - :param v: Value to assign. - :type v: :class:`basestring` - - .. versionadded: 0.5.0 - """ - assertions.assert_string(key=k, value=v) - image = self.image - r = library.MagickSetImageProperty(image.wand, binary(k), binary(v)) - if not r: - image.raise_exception() - return v - - def __delitem__(self, k): - """ - :param k: Metadata header name string. - :type k: :class:`basestring` - - .. versionadded: 0.5.0 - """ - assertions.assert_string(key=k) - image = self.image - r = library.MagickDeleteImageProperty(image.wand, binary(k)) - if not r: - image.raise_exception() - - def __iter__(self): - image = self.image - num = ctypes.c_size_t() - props_p = library.MagickGetImageProperties(image.wand, b'', num) - props = [text(ctypes.string_at(props_p[i])) for i in xrange(num.value)] - props_p = library.MagickRelinquishMemory(props_p) - return iter(props) - - def __len__(self): - image = self.image - num = ctypes.c_size_t() - props_p = library.MagickGetImageProperties(image.wand, b'', num) - props_p = library.MagickRelinquishMemory(props_p) - return num.value - - -class ArtifactTree(ImageProperty, abc.MutableMapping): - """Splay tree to map image artifacts. Values defined here - are intended to be used elseware, and will not be written - to the encoded image. - - For example:: - - # Omit timestamp from PNG file headers. - with Image(filename='input.png') as img: - img.artifacts['png:exclude-chunks'] = 'tIME' - img.save(filename='output.png') - - :param image: an image instance - :type image: :class:`Image` - - .. note:: - - You don't have to use this by yourself. - Use :attr:`Image.artifacts` property instead. - - .. versionadded:: 0.5.0 - """ - - def __init__(self, image): - if not isinstance(image, Image): - raise TypeError('expected a wand.image.Image instance, ' - 'not ' + repr(image)) - super(ArtifactTree, self).__init__(image) - - def __getitem__(self, k): - """ - :param k: Metadata header name string. - :type k: :class:`basestring` - :returns: a header value string - :rtype: :class:`str` - - .. versionadded: 0.5.0 - """ - assertions.assert_string(key=k) - image = self.image - vs = b'' - vp = library.MagickGetImageArtifact(image.wand, binary(k)) - if vp: - vs = text(ctypes.string_at(vp)) - vp = library.MagickRelinquishMemory(vp) - if len(vs) < 1: - vp = library.MagickGetImageProperty(image.wand, binary(k)) - if vp: - vs = text(ctypes.string_at(vp)) - vp = library.MagickRelinquishMemory(vp) - else: - vs = None - return vs - - def __setitem__(self, k, v): - """ - :param k: Metadata header name string. - :type k: :class:`basestring` - :param v: Value to assign. - :type v: :class:`basestring` - - .. versionadded: 0.5.0 - """ - assertions.assert_string(key=k, value=v) - image = self.image - r = library.MagickSetImageArtifact(image.wand, binary(k), binary(v)) - if not r: # pragma: no cover - image.raise_exception() - return v - - def __delitem__(self, k): - """ - :param k: Metadata header name string. - :type k: :class:`basestring` - - .. versionadded: 0.5.0 - """ - assertions.assert_string(key=k) - image = self.image - r = library.MagickDeleteImageArtifact(image.wand, binary(k)) - if not r: # pragma: no cover - image.raise_exception() - - def __iter__(self): - image = self.image - num = ctypes.c_size_t(0) - art_p = library.MagickGetImageArtifacts(image.wand, b'', num) - props = [text(ctypes.string_at(art_p[i])) for i in xrange(num.value)] - art_p = library.MagickRelinquishMemory(art_p) - return iter(props) - - def __len__(self): - image = self.image - num = ctypes.c_size_t(0) - art_p = library.MagickGetImageArtifacts(image.wand, b'', num) - art_p = library.MagickRelinquishMemory(art_p) - return num.value - - -class ProfileDict(ImageProperty, abc.MutableMapping): - """The mapping table of embedded image profiles. - - Use this to get, set, and delete whole profile payloads on an image. Each - payload is a raw binary string. - - For example:: - - with Image(filename='photo.jpg') as img: - # Extract EXIF - with open('exif.bin', 'wb') as payload: - payload.write(img.profiles['exif']) - # Import ICC - with open('color_profile.icc', 'rb') as payload: - img.profiles['icc'] = payload.read() - # Remove XMP - del imp.profiles['xmp'] - - .. seealso:: - - `Embedded Image Profiles`__ for a list of supported profiles. - - __ https://imagemagick.org/script/formats.php#embedded - - .. versionadded:: 0.5.1 - """ - def __init__(self, image): - if not isinstance(image, Image): - raise TypeError('expected a wand.image.Image instance, ' - 'not ' + repr(image)) - super(ProfileDict, self).__init__(image) - - def __delitem__(self, k): - assertions.assert_string(key=k) - num = ctypes.c_size_t(0) - profile_p = library.MagickRemoveImageProfile(self.image.wand, - binary(k), num) - profile_p = library.MagickRelinquishMemory(profile_p) - - def __getitem__(self, k): - assertions.assert_string(key=k) - num = ctypes.c_size_t(0) - return_profile = None - profile_p = library.MagickGetImageProfile(self.image.wand, - binary(k), num) - if num.value > 0: - return_profile = ctypes.string_at(profile_p, num.value) - profile_p = library.MagickRelinquishMemory(profile_p) - return return_profile - - def __iter__(self): - num = ctypes.c_size_t(0) - prop = library.MagickGetImageProfiles(self.image.wand, b'', num) - profiles = [text(ctypes.string_at(prop[i])) for i in xrange(num.value)] - prop = library.MagickRelinquishMemory(prop) - return iter(profiles) - - def __len__(self): - num = ctypes.c_size_t(0) - profiles_p = library.MagickGetImageProfiles(self.image.wand, b'', num) - profiles_p = library.MagickRelinquishMemory(profiles_p) - return num.value - - def __setitem__(self, k, v): - assertions.assert_string(key=k) - if not isinstance(v, binary_type): - raise TypeError('value must be a binary string, not ' + repr(v)) - r = library.MagickSetImageProfile(self.image.wand, - binary(k), v, len(v)) - if not r: - self.image.raise_exception() - - -class ChannelImageDict(ImageProperty, abc.Mapping): - """The mapping table of separated images of the particular channel - from the image. - - :param image: an image instance - :type image: :class:`Image` - - .. note:: - - You don't have to use this by yourself. - Use :attr:`Image.channel_images` property instead. - - .. versionadded:: 0.3.0 - - """ - - def __iter__(self): - return iter(CHANNELS) - - def __len__(self): - return len(CHANNELS) - - def __getitem__(self, channel): - c = CHANNELS[channel] - img = self.image.clone() - if library.MagickSeparateImageChannel: - succeeded = library.MagickSeparateImageChannel(img.wand, c) - else: - succeeded = library.MagickSeparateImage(img.wand, c) - if not succeeded: - try: - img.raise_exception() - except WandException: - img.close() - raise - return img - - -class ChannelDepthDict(ImageProperty, abc.Mapping): - """The mapping table of channels to their depth. - - :param image: an image instance - :type image: :class:`Image` - - .. note:: - - You don't have to use this by yourself. - Use :attr:`Image.channel_depths` property instead. - - .. versionadded:: 0.3.0 - - """ - - def __iter__(self): - return iter(CHANNELS) - - def __len__(self): - return len(CHANNELS) - - def __getitem__(self, channel): - c = CHANNELS[channel] - if library.MagickGetImageChannelDepth: - depth = library.MagickGetImageChannelDepth(self.image.wand, c) - else: - mask = 0 - if c != 0: - mask = library.MagickSetImageChannelMask(self.image.wand, c) - depth = library.MagickGetImageDepth(self.image.wand) - if mask != 0: - library.MagickSetImageChannelMask(self.image.wand, mask) - return int(depth) - - -class HistogramDict(abc.Mapping): - """Specialized mapping object to represent color histogram. - Keys are colors, and values are the number of pixels. - - :param image: the image to get its histogram - :type image: :class:`BaseImage` - - .. versionadded:: 0.3.0 - - """ - - def __init__(self, image): - self.size = ctypes.c_size_t() - self.pixels = library.MagickGetImageHistogram( - image.wand, - ctypes.byref(self.size) - ) - self.counts = None - - def __del__(self): - if self.pixels: - self.pixels = library.DestroyPixelWands(self.pixels, - self.size.value) - - def __len__(self): - if self.counts is None: - return self.size.value - return len(self.counts) - - def __iter__(self): - if self.counts is None: - self._build_counts() - return iter(self.counts) - - def __getitem__(self, color): - if self.counts is None: - self._build_counts() - if isinstance(color, string_type): - color = Color(color) - assertions.assert_color(color=color) - return self.counts[color] - - def _build_counts(self): - self.counts = {} - for i in xrange(self.size.value): - color_count = library.PixelGetColorCount(self.pixels[i]) - color = Color.from_pixelwand(self.pixels[i]) - self.counts[color] = color_count - - -class ConnectedComponentObject(object): - """Generic Python wrapper to translate - :c:type:`CCObjectInfo` structure into a class describing objects found - within an image. This class is generated by - :meth:`Image.connected_components() - ` method. - - .. versionadded:: 0.5.5 - .. versionchanged:: 0.6.3 - Added :attr:`merge` & :attr:`metric` for ImageMagick 7.0.10 - .. versionchanged:: 0.6.8 - Added :attr:`key` property for ImageMagick 7.1.0 - """ - #: (:class:`numbers.Integral`) Serialized object identifier - #: starting at `0`. - _id = None - - #: (:class:`numbers.Integral`) Width of objects minimum - #: bounding rectangle. - width = None - - #: (:class:`numbers.Integral`) Height of objects minimum - #: bounding rectangle. - height = None - - #: (:class:`numbers.Integral`) X offset of objects minimum - #: bounding rectangle. - left = None - - #: (:class:`numbers.Integral`) Y offset of objects minimum - #: bounding rectangle. - top = None - - #: (:class:`numbers.Real`) X offset of objects centroid. - center_x = None - - #: (:class:`numbers.Real`) Y offset of objects centroid. - center_y = None - - #: (:class:`numbers.Real`) Quantity of pixels that make-up - #: the objects shape. - area = None - - #: (:class:`~wand.color.Color`) The average color of the - #: shape. - mean_color = None - - #: (:class:`bool`) Object merge flag. Only avaliable after - #: ImageMagick-7.0.10. - #: ..versionadded:: 0.6.3 - merge = None - - #: (:class:`list`) List of doubles used by metric. Only avaliable after - #: ImageMagick-7.0.10. - #: ..versionadded:: 0.6.3 - metric = None - - def __init__(self, cc_object=None): - if isinstance(cc_object, CCObjectInfo): - self.clone_from_cc_object_info(cc_object) - if isinstance(cc_object, CCObjectInfo70A): - self.clone_from_cc_object_info(cc_object) - self.clone_from_extra_70A_info(cc_object) - if isinstance(cc_object, CCObjectInfo710): - self.clone_from_cc_object_info(cc_object) - self.clone_from_extra_70A_info(cc_object) - self.clone_from_extra_710_info(cc_object) - - @property - def size(self): - """(:class:`tuple` (:attr:`width`, :attr:`height`)) - Minimum bounding rectangle.""" - return self.width, self.height - - @property - def offset(self): - """(:class:`tuple` (:attr:`left`, :attr:`top`)) - Position of objects minimum bounding rectangle.""" - return self.left, self.top - - @property - def centroid(self): - """(:class:`tuple` (:attr:`center_x`, :attr:`center_y`)) - Center of object.""" - return self.center_x, self.center_y - - def clone_from_cc_object_info(self, cc_object): - """Copy data from :class:`~wand.cdefs.structures.CCObjectInfo`.""" - self._id = cc_object._id - self.width = cc_object.bounding_box.width - self.height = cc_object.bounding_box.height - self.left = cc_object.bounding_box.x - self.top = cc_object.bounding_box.y - self.center_x = cc_object.centroid.x - self.center_y = cc_object.centroid.y - self.area = cc_object.area - pinfo_size = ctypes.sizeof(PixelInfo) - raw_buffer = ctypes.create_string_buffer(pinfo_size) - ctypes.memmove(raw_buffer, - ctypes.byref(cc_object.color), - pinfo_size) - self.mean_color = Color(raw=raw_buffer) - - def clone_from_extra_70A_info(self, cc_object): - """Copy the additional values from CCObjectInfo structure. This is the - :attr:`merge` & :attr:`metric` properties added in ImageMagick 7.0.10. - - .. versionadded:: 0.6.3 - """ - self.merge = cc_object.merge - self.metric = [] - for i in range(cc_object.CCMaxMetrics): - self.metric.append(cc_object.metric[i]) - - def clone_from_extra_710_info(self, cc_object): - """Copy additional value from CCObjectInfo structure for properties - added to ImageMagick 7.1.0. - - .. versionadded:: 0.6.8 - """ - self.key = cc_object.key - - def __repr__(self): - fmt = ("{name}({_id}: {width}x{height}+{left}+{top} {center_x:.2f}," - "{center_y:.2f} {area:.0f} {mean_color})") - return fmt.format(name=self.__class__.__name__, **self.__dict__) - - -class ClosedImageError(DestroyedResourceError): - """An error that rises when some code tries access to an already closed - image. - - """ diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/resource.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/resource.py deleted file mode 100644 index 991599e..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/resource.py +++ /dev/null @@ -1,381 +0,0 @@ -""":mod:`wand.resource` --- Global resource management -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There is the global resource to manage in MagickWand API. This module -implements automatic global resource management through reference counting. - -""" -import atexit -import contextlib -import ctypes -import warnings - -from .api import library -from .compat import abc, string_type -from .exceptions import TYPE_MAP, WandException -from .version import MAGICK_VERSION_NUMBER - -__all__ = ('genesis', 'limits', 'shutdown', 'terminus', - 'DestroyedResourceError', 'Resource', 'ResourceLimits') - - -def genesis(): - """Instantiates the MagickWand API. - - .. warning:: - - Don't call this function directly. Use :func:`increment_refcount()` and - :func:`decrement_refcount()` functions instead. - - """ - library.MagickWandGenesis() - - -def terminus(): - """Cleans up the MagickWand API. - - .. warning:: - - Don't call this function directly. Use :func:`increment_refcount()` and - :func:`decrement_refcount()` functions instead. - - """ - if library.IsMagickWandInstantiated is None: # pragma no cover - library.MagickWandTerminus() - elif library.IsMagickWandInstantiated(): - library.MagickWandTerminus() - - -allocation_map = {} - - -def allocate_ref(addr, deallocator): - global allocation_map - if len(allocation_map) == 0: - genesis() - if addr: - allocation_map[addr] = deallocator - - -def deallocate_ref(addr): - global allocation_map - if addr in list(allocation_map): - deallocator = allocation_map.pop(addr) - if callable(deallocator): - deallocator(addr) - - -@atexit.register -def shutdown(): - global allocation_map - for addr in list(allocation_map): - try: - deallocator = allocation_map.pop(addr) - if callable(deallocator): - deallocator(addr) - except KeyError: - pass - terminus() - - -class Resource(object): - """Abstract base class for MagickWand object that requires resource - management. Its all subclasses manage the resource semiautomatically - and support :keyword:`with` statement as well:: - - with Resource() as resource: - # use the resource... - pass - - It doesn't implement constructor by itself, so subclasses should - implement it. Every constructor should assign the pointer of its - resource data into :attr:`resource` attribute inside of :keyword:`with` - :meth:`allocate()` context. For example:: - - class Pizza(Resource): - '''My pizza yummy.''' - - def __init__(self): - with self.allocate(): - self.resource = library.NewPizza() - - .. versionadded:: 0.1.2 - - """ - - #: (:class:`ctypes.CFUNCTYPE`) The :mod:`ctypes` predicate function - #: that returns whether the given pointer (that contains a resource data - #: usually) is a valid resource. - #: - #: .. note:: - #: - #: It is an abstract attribute that has to be implemented - #: in the subclass. - c_is_resource = NotImplemented - - #: (:class:`ctypes.CFUNCTYPE`) The :mod:`ctypes` function that destroys - #: the :attr:`resource`. - #: - #: .. note:: - #: - #: It is an abstract attribute that has to be implemented - #: in the subclass. - c_destroy_resource = NotImplemented - - #: (:class:`ctypes.CFUNCTYPE`) The :mod:`ctypes` function that gets - #: an exception from the :attr:`resource`. - #: - #: .. note:: - #: - #: It is an abstract attribute that has to be implemented - #: in the subclass. - c_get_exception = NotImplemented - - #: (:class:`ctypes.CFUNCTYPE`) The :mod:`ctypes` function that clears - #: an exception of the :attr:`resource`. - #: - #: .. note:: - #: - #: It is an abstract attribute that has to be implemented - #: in the subclass. - c_clear_exception = NotImplemented - - @property - def resource(self): - """Internal pointer to the resource instance. It may raise - :exc:`DestroyedResourceError` when the resource has destroyed already. - - """ - if getattr(self, 'c_resource', None) is None: - raise DestroyedResourceError(repr(self) + ' is destroyed already') - return self.c_resource - - @resource.setter - def resource(self, resource): - # Delete the existing resource if there is one - if getattr(self, 'c_resource', None): - self.destroy() - - if self.c_is_resource(resource): - self.c_resource = resource - allocate_ref(self.c_resource, self.c_destroy_resource) - else: - raise TypeError(repr(resource) + ' is an invalid resource') - - @resource.deleter - def resource(self): - if getattr(self, 'c_resource', None): - deallocate_ref(self.c_resource) - self.c_resource = None - - @contextlib.contextmanager - def allocate(self): - """Allocates the memory for the resource explicitly. Its subclasses - should assign the created resource into :attr:`resource` attribute - inside of this context. For example:: - - with resource.allocate(): - resource.resource = library.NewResource() - - """ - # As of 0x710, we must call MagickWandGenesis before allocate of - # Wand's Resource & ImageMagick PixelWand. - genesis() - yield self - - def destroy(self): - """Cleans up the resource explicitly. If you use the resource in - :keyword:`with` statement, it was called implicitly so have not to - call it. - - """ - del self.resource - - def get_exception(self): - """Gets a current exception instance. - - :returns: a current exception. it can be ``None`` as well if any - errors aren't occurred - :rtype: :class:`wand.exceptions.WandException` - - """ - severity = ctypes.c_int() - desc = self.c_get_exception(self.resource, ctypes.byref(severity)) - if severity.value == 0: - if desc: - desc = library.MagickRelinquishMemory(desc) - return - self.c_clear_exception(self.resource) - exc_cls = TYPE_MAP[severity.value] - if desc: - message = ctypes.string_at(desc) - desc = library.MagickRelinquishMemory(desc) - else: - message = b'' - if not isinstance(message, string_type): - message = message.decode(errors='replace') - return exc_cls(message) - - def raise_exception(self, stacklevel=1): - """Raises an exception or warning if it has occurred.""" - e = self.get_exception() - if isinstance(e, Warning): - warnings.warn(e, stacklevel=stacklevel + 1) - elif isinstance(e, Exception): - raise e - - def make_blob(self, format=None): - raise NotImplementedError - - def __enter__(self): - return self - - def __exit__(self, type, value, traceback): - self.destroy() - - def __del__(self): - try: - self.destroy() - except DestroyedResourceError: - pass - - -class DestroyedResourceError(WandException, ReferenceError, AttributeError): - """An error that rises when some code tries access to an already - destroyed resource. - - .. versionchanged:: 0.3.0 - It becomes a subtype of :exc:`wand.exceptions.WandException`. - - """ - - -class ResourceLimits(abc.MutableMapping): - """Wrapper for MagickCore resource limits. - Useful for dynamically reducing system resources before attempting risky, - or slow running, :class:`~wand.image.Image` operations. - - For example:: - - from wand.image import Image - from wand.resource import limits - - # Use 100MB of ram before writing temp data to disk. - limits['memory'] = 1024 * 1024 * 100 - # Reject images larger than 1000x1000. - limits['width'] = 1000 - limits['height'] = 1000 - - # Debug resources used. - with Image(filename='user.jpg') as img: - print('Using {0} of {1} memory'.format(limits.resource('memory'), - limits['memory'])) - - # Dump list of all limits. - for label in limits: - print('{0} => {1}'.format(label, limits[label])) - - Available resource keys: - - - ``'area'`` - Maximum `width * height` of a pixel cache before writing to - disk. - - ``'disk'`` - Maximum bytes used by pixel cache on disk before exception - is thrown. - - ``'file'`` - Maximum cache files opened at any given time. - - ``'height'`` - Maximum height of image before exception is thrown. - - ``'list_length'`` - Maximum images in sequence. Only available with - recent version of ImageMagick. - - ``'map'`` - Maximum memory map in bytes to allocated for pixel cache - before using disk. - - ``'memory'`` - Maximum bytes to allocated for pixel cache before using - disk. - - ``'thread'`` - Maximum parallel task sub-routines can spawn - if using - OpenMP. - - ``'throttle'`` - Total milliseconds to yield to CPU - if possible. - - ``'time'`` - Maximum seconds before exception is thrown. - - ``'width'`` - Maximum width of image before exception is thrown. - - .. versionadded:: 0.5.1 - """ - - #: (:class:`tuple`) List of available resource types for ImageMagick-6. - _limits6 = ('undefined', 'area', 'disk', 'file', 'map', 'memory', 'thread', - 'time', 'throttle', 'width', 'height') - - #: (:class:`tuple`) List of available resource types for ImageMagick-7. - _limits7 = ('undefined', 'area', 'disk', 'file', 'height', 'map', 'memory', - 'thread', 'throttle', 'time', 'width', 'list_length') - - def __init__(self): - if MAGICK_VERSION_NUMBER < 0x700: - self.limits = self._limits6 - else: - self.limits = self._limits7 - - def __getitem__(self, r): - return self.get_resource_limit(r) - - def __setitem__(self, r, v): - self.set_resource_limit(r, v) - - def __delitem__(self, r): - self[r] = 0 - - def __iter__(self): - return iter(self.limits) - - def __len__(self): - return len(self.limits) - - def _to_idx(self, resource): - """Helper method to map resource string to enum value.""" - return self.limits.index(resource) - - def resource(self, resource): - """Get the current value for the resource type. - - :param resource: Resource type. - :type resource: :class:`basestring` - :rtype: :class:`numeric.Integral` - - .. versionadded:: 0.5.1 - """ - return library.MagickGetResource(self._to_idx(resource)) - - def get_resource_limit(self, resource): - """Get the current limit for the resource type. - - :param resource: Resource type. - :type resource: :class:`basestring` - :rtype: :class:`numeric.Integral` - - .. versionadded:: 0.5.1 - """ - genesis() - return library.MagickGetResourceLimit(self._to_idx(resource)) - - def set_resource_limit(self, resource, limit): - """Sets a new limit for resource type. - - .. note:: - - The new limit value must be equal to or less than the maximum - limit defined by the :file:`policy.xml`. Any values set outside - normal bounds will be ignored silently. - - :param resource: Resource type. - :type resource: :class:`basestring` - :param limit: New limit value. - :type limit: :class:`numeric.Integral` - - .. versionadded:: 0.5.1 - """ - genesis() - ull = ctypes.c_ulonglong(limit) - library.MagickSetResourceLimit(self._to_idx(resource), ull) - - -#: (:class:`ResourceLimits`) Helper to get & set Magick Resource Limits. -#: -#: .. versionadded:: 0.5.1 -limits = ResourceLimits() diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/sequence.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/sequence.py deleted file mode 100644 index cfc6b60..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/sequence.py +++ /dev/null @@ -1,358 +0,0 @@ -""":mod:`wand.sequence` --- Sequences -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded:: 0.3.0 - -""" -import contextlib -import ctypes -import numbers - -from .api import libmagick, library -from .compat import abc, binary, xrange -from .image import BaseImage, ImageProperty -from .version import MAGICK_VERSION_INFO - -__all__ = 'Sequence', 'SingleImage' - - -class Sequence(ImageProperty, abc.MutableSequence): - """The list-like object that contains every :class:`SingleImage` - in the :class:`~wand.image.Image` container. It implements - :class:`collections.abc.Sequence` protocol. - - .. versionadded:: 0.3.0 - - """ - - def __init__(self, image): - super(Sequence, self).__init__(image) - self.instances = [] - - @property - def current_index(self): - """(:class:`numbers.Integral`) The current index of - its internal iterator. - - .. note:: - - It's only for internal use. - - """ - return library.MagickGetIteratorIndex(self.image.wand) - - @current_index.setter - def current_index(self, index): - library.MagickSetIteratorIndex(self.image.wand, index) - - @contextlib.contextmanager - def index_context(self, index): - """Scoped setter of :attr:`current_index`. Should be - used for :keyword:`with` statement e.g.:: - - with image.sequence.index_context(3): - print(image.size) - - .. note:: - - It's only for internal use. - - """ - index = self.validate_position(index) - tmp_idx = self.current_index - self.current_index = index - yield index - self.current_index = tmp_idx - - def __len__(self): - return library.MagickGetNumberImages(self.image.wand) - - def validate_position(self, index): - if not isinstance(index, numbers.Integral): - raise TypeError('index must be integer, not ' + repr(index)) - length = len(self) - if index >= length or index < -length: - raise IndexError( - 'out of index: {0} (total: {1})'.format(index, length) - ) - if index < 0: - index += length - return index - - def validate_slice(self, slice_, as_range=False): - if not (slice_.step is None or slice_.step == 1): - raise ValueError('slicing with step is unsupported') - length = len(self) - if slice_.start is None: - start = 0 - elif slice_.start < 0: - start = length + slice_.start - else: - start = slice_.start - start = min(length, start) - if slice_.stop is None: - stop = 0 - elif slice_.stop < 0: - stop = length + slice_.stop - else: - stop = slice_.stop - stop = min(length, stop or length) - return xrange(start, stop) if as_range else slice(start, stop, None) - - def __getitem__(self, index): - if isinstance(index, slice): - slice_ = self.validate_slice(index) - return [self[i] for i in xrange(slice_.start, slice_.stop)] - index = self.validate_position(index) - instances = self.instances - instances_length = len(instances) - if index < instances_length: - instance = instances[index] - if (instance is not None and - getattr(instance, 'c_resource', None) is not None): - return instance - else: - number_to_extend = index - instances_length + 1 - instances.extend(None for _ in xrange(number_to_extend)) - wand = self.image.wand - tmp_idx = library.MagickGetIteratorIndex(wand) - library.MagickSetIteratorIndex(wand, index) - image = library.GetImageFromMagickWand(wand) - exc = libmagick.AcquireExceptionInfo() - single_image = libmagick.CloneImages(image, binary(str(index)), exc) - libmagick.DestroyExceptionInfo(exc) - single_wand = library.NewMagickWandFromImage(single_image) - single_image = libmagick.DestroyImage(single_image) - library.MagickSetIteratorIndex(wand, tmp_idx) - instance = SingleImage(single_wand, self.image, image) - self.instances[index] = instance - return instance - - def __setitem__(self, index, image): - if isinstance(index, slice): - tmp_idx = self.current_index - slice_ = self.validate_slice(index) - del self[slice_] - self.extend(image, offset=slice_.start) - self.current_index = tmp_idx - else: - if not isinstance(image, BaseImage): - raise TypeError('image must be an instance of wand.image.' - 'BaseImage, not ' + repr(image)) - with self.index_context(index) as index: - if library.MagickHasNextImage(self.image.wand): - library.MagickAddImage(self.image.wand, image.wand) - library.MagickRemoveImage(self.image.wand) - else: - library.MagickRemoveImage(self.image.wand) - library.MagickAddImage(self.image.wand, image.wand) - - def __delitem__(self, index): - if isinstance(index, slice): - range_ = self.validate_slice(index, as_range=True) - for i in reversed(range_): - del self[i] - else: - with self.index_context(index) as index: - library.MagickRemoveImage(self.image.wand) - if index < len(self.instances): - del self.instances[index] - - def insert(self, index, image): - try: - index = self.validate_position(index) - except IndexError: - index = len(self) - if not isinstance(image, BaseImage): - raise TypeError('image must be an instance of wand.image.' - 'BaseImage, not ' + repr(image)) - if not self: - library.MagickAddImage(self.image.wand, image.wand) - elif index == 0: - tmp_idx = self.current_index - self_wand = self.image.wand - wand = image.sequence[0].wand - try: - # Prepending image into the list using MagickSetFirstIterator() - # and MagickAddImage() had not worked properly, but was fixed - # since 6.7.6-0 (rev7106). - if MAGICK_VERSION_INFO >= (6, 7, 6, 0): - library.MagickSetFirstIterator(self_wand) - library.MagickAddImage(self_wand, wand) - else: # pragma: no cover - self.current_index = 0 - library.MagickAddImage(self_wand, - self.image.sequence[0].wand) - self.current_index = 0 - library.MagickAddImage(self_wand, wand) - self.current_index = 0 - library.MagickRemoveImage(self_wand) - finally: - self.current_index = tmp_idx - else: - with self.index_context(index - 1): - library.MagickAddImage(self.image.wand, image.sequence[0].wand) - self.instances.insert(index, None) - - def append(self, image): - if not isinstance(image, BaseImage): - raise TypeError('image must be an instance of wand.image.' - 'BaseImage, not ' + repr(image)) - wand = self.image.wand - tmp_idx = self.current_index - try: - library.MagickSetLastIterator(wand) - library.MagickAddImage(wand, image.sequence[0].wand) - finally: - self.current_index = tmp_idx - self.instances.append(None) - - def extend(self, images, offset=None): - tmp_idx = self.current_index - wand = self.image.wand - length = 0 - try: - if offset is None: - library.MagickSetLastIterator(self.image.wand) - else: - if offset == 0: - images = iter(images) - self.insert(0, next(images)) - offset += 1 - self.current_index = offset - 1 - if isinstance(images, type(self)): - library.MagickAddImage(wand, images.image.wand) - length = len(images) - else: - delta = 1 if MAGICK_VERSION_INFO >= (6, 7, 6, 0) else 2 - for image in images: - if not isinstance(image, BaseImage): - raise TypeError( - 'images must consist of only instances of ' - 'wand.image.BaseImage, not ' + repr(image) - ) - else: - library.MagickAddImage(wand, image.sequence[0].wand) - self.instances = [] - if offset is None: - library.MagickSetLastIterator(self.image.wand) - else: - self.current_index += delta - length += 1 - finally: - self.current_index = tmp_idx - null_list = [None] * length - if offset is None: - self.instances[offset:] = null_list - else: - self.instances[offset:offset] = null_list - - def _repr_png_(self): # pragma: no cover - library.MagickResetIterator(self.image.wand) - repr_wand = library.MagickAppendImages(self.image.wand, 1) - length = ctypes.c_size_t() - blob_p = library.MagickGetImagesBlob(repr_wand, - ctypes.byref(length)) - if blob_p and length.value: - blob = ctypes.string_at(blob_p, length.value) - blob_p = library.MagickRelinquishMemory(blob_p) - return blob - else: - return None - - -class SingleImage(BaseImage): - """Each single image in :class:`~wand.image.Image` container. - For example, it can be a frame of GIF animation. - - Note that all changes on single images are invisible to their - containers unless they are altered a ``with ...`` context manager. - - with Image(filename='animation.gif') as container: - with container.sequence[0] as frame: - frame.negate() - - .. versionadded:: 0.3.0 - - .. versionchanged:: 0.5.1 - Only sync changes of a :class:`SingleImage` when exiting a ``with ...`` - context. Not when parent :class:`~wand.image.Image` closes. - """ - - #: (:class:`wand.image.Image`) The container image. - container = None - - def __init__(self, wand, container, c_original_resource): - super(SingleImage, self).__init__(wand) - self.container = container - self.c_original_resource = c_original_resource - self._delay = None - - @property - def sequence(self): - return self, - - @property - def index(self): - """(:class:`numbers.Integral`) The index of the single image in - the :attr:`container` image. - - """ - wand = self.container.wand - library.MagickResetIterator(wand) - image = library.GetImageFromMagickWand(wand) - i = 0 - while self.c_original_resource != image and image: - image = libmagick.GetNextImageInList(image) - i += 1 - assert image - assert self.c_original_resource == image - return i - - @property - def delay(self): - """(:class:`numbers.Integral`) The delay to pause before display - the next image (in the :attr:`~wand.image.BaseImage.sequence` of - its :attr:`container`). It's hundredths of a second. - - """ - if self._delay is None: - container = self.container - with container.sequence.index_context(self.index): - self._delay = library.MagickGetImageDelay(container.wand) - return self._delay - - @delay.setter - def delay(self, delay): - if not isinstance(delay, numbers.Integral): - raise TypeError('delay must be an integer, not ' + repr(delay)) - elif delay < 0: - raise ValueError('delay cannot be less than zero') - container = self.container - with container.sequence.index_context(self.index): - library.MagickSetImageDelay(container.wand, delay) - self._delay = delay - - def _sync_container_sequence(self): - """If instances was flagged as :attr:`dirty` by any manipulation - methods, then this instance will overwrite :attr:`container` internal - version at :attr:`index`. - - .. versionadded:: 0.5.1 - """ - if self.dirty: - self.container.sequence[self.index] = self - self.dirty = False # Reset dirty flag - - def __exit__(self, type_, value, traceback): - self._sync_container_sequence() - super(SingleImage, self).__exit__(type_, value, traceback) - - def __repr__(self): - cls = type(self) - if getattr(self, 'c_resource', None) is None: - return '<{0}.{1}: (closed)>'.format(cls.__module__, cls.__name__) - return '<{0}.{1}: {2} ({3}x{4})>'.format( - cls.__module__, cls.__name__, - self.signature[:7], self.width, self.height - ) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/version.py b/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/version.py deleted file mode 100644 index 80c38d7..0000000 --- a/MangaJaNaiConverterGui/chaiNNer/python/Lib/site-packages/wand/version.py +++ /dev/null @@ -1,303 +0,0 @@ -""":mod:`wand.version` --- Version data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -You can find the current version in the command line interface: - -.. sourcecode:: console - - $ python -m wand.version - 0.0.0 - $ python -m wand.version --verbose - Wand 0.0.0 - ImageMagick 6.7.7-6 2012-06-03 Q16 http://www.imagemagick.org - $ python -m wand.version --config | grep CC | cut -d : -f 2 - gcc -std=gnu99 -std=gnu99 - $ python -m wand.version --fonts | grep Helvetica - Helvetica - Helvetica-Bold - Helvetica-Light - Helvetica-Narrow - Helvetica-Oblique - $ python -m wand.version --formats | grep CMYK - CMYK - CMYKA - -.. versionadded:: 0.2.0 - The command line interface. - -.. versionadded:: 0.2.2 - The ``--verbose``/``-v`` option which also prints ImageMagick library - version for CLI. - -.. versionadded:: 0.4.1 - The ``--fonts``, ``--formats``, & ``--config`` option allows printing - additional information about ImageMagick library. - -""" -from __future__ import print_function - -import ctypes -import datetime -import re -import sys - -try: - from .api import libmagick, library -except ImportError: # pragma: no cover - libmagick = None -from .compat import binary, string_type, text - - -__all__ = ('VERSION', 'VERSION_INFO', 'MAGICK_VERSION', - 'MAGICK_VERSION_DELEGATES', 'MAGICK_VERSION_FEATURES', - 'MAGICK_VERSION_INFO', 'MAGICK_VERSION_NUMBER', - 'MAGICK_RELEASE_DATE', 'MAGICK_RELEASE_DATE_STRING', 'MAGICK_HDRI', - 'QUANTUM_DEPTH', 'QUANTUM_RANGE', 'QUANTUM_SCALE', - 'configure_options', 'fonts', 'formats') - -#: (:class:`tuple`) The version tuple e.g. ``(0, 1, 2)``. -#: -#: .. versionchanged:: 0.1.9 -#: Becomes :class:`tuple`. (It was string before.) -VERSION_INFO = (0, 6, 11) - -#: (:class:`basestring`) The version string e.g. ``'0.1.2'``. -#: -#: .. versionchanged:: 0.1.9 -#: Becomes string. (It was :class:`tuple` before.) -VERSION = '{0}.{1}.{2}'.format(*VERSION_INFO) - -if libmagick: - c_magick_version = ctypes.c_size_t() - #: (:class:`basestring`) The version string of the linked ImageMagick - #: library. The exactly same string to the result of - #: :c:func:`GetMagickVersion` function. - #: - #: Example:: - #: - #: 'ImageMagick 6.7.7-6 2012-06-03 Q16 http://www.imagemagick.org' - #: - #: .. versionadded:: 0.2.1 - MAGICK_VERSION = text( - libmagick.GetMagickVersion(ctypes.byref(c_magick_version)) - ) - - #: (:class:`numbers.Integral`) The version number of the linked - #: ImageMagick library. - #: - #: .. versionadded:: 0.2.1 - MAGICK_VERSION_NUMBER = c_magick_version.value - - _match = re.match(r'^ImageMagick\s+(\d+)\.(\d+)\.(\d+)(?:-(\d+))?', - MAGICK_VERSION) - - #: (:class:`basestring`) A string of all delegates enabled. - #: This value is identical to what is returned by - #: :c:func:`GetMagickDelegates` - #: - #: Set to empty string if the system uses an older version of - #: ImageMagick-6, or does not support :c:func:`GetMagickDelegates`. - #: - #: .. versionadded:: 0.5.0 - if libmagick.GetMagickDelegates: # pragma: no cover - MAGICK_VERSION_DELEGATES = text(libmagick.GetMagickDelegates()) - else: # pragma: no cover - MAGICK_VERSION_DELEGATES = "" - - #: (:class:`basestring`) A string of all features enabled. - #: This value is identical to what is returned by - #: :c:func:`GetMagickFeatures` - #: - #: .. versionadded:: 0.5.0 - MAGICK_VERSION_FEATURES = text(libmagick.GetMagickFeatures()) - - #: (:class:`tuple`) The version tuple e.g. ``(6, 7, 7, 6)`` of - #: :const:`MAGICK_VERSION`. - #: - #: .. versionadded:: 0.2.1 - MAGICK_VERSION_INFO = tuple(int(v or 0) for v in _match.groups()) - - #: (:class:`basestring`) The date string e.g. ``'2012-06-03'`` of - #: :const:`MAGICK_RELEASE_DATE_STRING`. This value is the exactly same - #: string to the result of :c:func:`GetMagickReleaseDate` function. - #: - #: .. versionadded:: 0.2.1 - MAGICK_RELEASE_DATE_STRING = text(libmagick.GetMagickReleaseDate()) - - if MAGICK_RELEASE_DATE_STRING: - _match = re.match(r'^(\d{4})-?(\d\d)-?(\d\d)$', - MAGICK_RELEASE_DATE_STRING) - #: (:class:`datetime.date`) The release date of the linked ImageMagick - #: library. Equivalent to the result of :c:func:`GetMagickReleaseDate` - #: function. - #: - #: .. versionadded:: 0.2.1 - MAGICK_RELEASE_DATE = datetime.date(*map(int, _match.groups())) - - c_quantum_depth = ctypes.c_size_t() - libmagick.GetMagickQuantumDepth(ctypes.byref(c_quantum_depth)) - #: (:class:`numbers.Integral`) The quantum depth configuration of - #: the linked ImageMagick library. One of 8, 16, 32, or 64. - #: - #: .. versionadded:: 0.3.0 - QUANTUM_DEPTH = c_quantum_depth.value - - c_quantum_range = ctypes.c_size_t() - libmagick.GetMagickQuantumRange(ctypes.byref(c_quantum_range)) - #: (:class:`numbers.Integral`) The quantum range configuration of - #: the linked ImageMagick library. - #: - #: .. versionadded:: 0.5.0 - QUANTUM_RANGE = c_quantum_range.value - - #: (:class:`numbers.Real`) The quantum scale of the linked ImageMagick - #: library. This is calculated as `1.0 / QUANTUM_RANGE`. - #: - #: .. versionadded:: 0.6.8 - QUANTUM_SCALE = 1.0 / float(QUANTUM_RANGE) - - #: (:class:`bool`) True if ImageMagick is compiled for High Dynamic - #: Range Image. - MAGICK_HDRI = 'HDRI' in MAGICK_VERSION_FEATURES - - del c_magick_version, _match, c_quantum_depth, c_quantum_range - - -def configure_options(pattern='*'): - """ - Queries ImageMagick library for configurations options given at - compile-time. - - Example: Find where the ImageMagick documents are installed:: - - >>> from wand.version import configure_options - >>> configure_options('DOC*') - {'DOCUMENTATION_PATH': '/usr/local/share/doc/ImageMagick-6'} - - :param pattern: A term to filter queries against. Supports wildcard '*' - characters. Default patterns '*' for all options. - :type pattern: :class:`basestring` - :returns: Directory of configuration options matching given pattern - :rtype: :class:`collections.defaultdict` - """ - if not isinstance(pattern, string_type): - raise TypeError('pattern must be a string, not ' + repr(pattern)) - # We must force init environment to load user config paths. - library.MagickWandGenesis() - pattern_p = ctypes.create_string_buffer(binary(pattern)) - config_count = ctypes.c_size_t(0) - configs = {} - configs_p = library.MagickQueryConfigureOptions(pattern_p, - ctypes.byref(config_count)) - for cursor in range(config_count.value): - config = ctypes.string_at(configs_p[cursor]) - val_p = library.MagickQueryConfigureOption(config) - if val_p: - configs[text(config)] = text(ctypes.string_at(val_p)) - val_p = library.MagickRelinquishMemory(val_p) - if configs_p: - configs_p = library.MagickRelinquishMemory(configs_p) - return configs - - -def fonts(pattern='*'): - """ - Queries ImageMagick library for available fonts. - - Available fonts can be configured by defining `types.xml`, - `type-ghostscript.xml`, or `type-windows.xml`. - Use :func:`wand.version.configure_options` to locate system search path, - and `resources `_ - article for defining xml file. - - Example: List all bold Helvetica fonts:: - - >>> from wand.version import fonts - >>> fonts('*Helvetica*Bold*') - ['Helvetica-Bold', 'Helvetica-Bold-Oblique', 'Helvetica-BoldOblique', - 'Helvetica-Narrow-Bold', 'Helvetica-Narrow-BoldOblique'] - - - :param pattern: A term to filter queries against. Supports wildcard '*' - characters. Default patterns '*' for all options. - :type pattern: :class:`basestring` - :returns: Sequence of matching fonts - :rtype: :class:`collections.Sequence` - """ - if not isinstance(pattern, string_type): - raise TypeError('pattern must be a string, not ' + repr(pattern)) - # We must force init environment to load user config paths. - library.MagickWandGenesis() - pattern_p = ctypes.create_string_buffer(binary(pattern)) - number_fonts = ctypes.c_size_t(0) - fonts = [] - fonts_p = library.MagickQueryFonts(pattern_p, - ctypes.byref(number_fonts)) - for cursor in range(number_fonts.value): - font = ctypes.string_at(fonts_p[cursor]) - fonts.append(text(font)) - if fonts_p: - fonts_p = library.MagickRelinquishMemory(fonts_p) - return fonts - - -def formats(pattern='*'): - """ - Queries ImageMagick library for supported formats. - - Example: List supported PNG formats:: - - >>> from wand.version import formats - >>> formats('PNG*') - ['PNG', 'PNG00', 'PNG8', 'PNG24', 'PNG32', 'PNG48', 'PNG64'] - - - :param pattern: A term to filter formats against. Supports wildcards '*' - characters. Default pattern '*' for all formats. - :type pattern: :class:`basestring` - :returns: Sequence of matching formats - :rtype: :class:`collections.Sequence` - """ - if not isinstance(pattern, string_type): - raise TypeError('pattern must be a string, not ' + repr(pattern)) - # We must force init environment to load user config paths. - library.MagickWandGenesis() - pattern_p = ctypes.create_string_buffer(binary(pattern)) - number_formats = ctypes.c_size_t(0) - formats = [] - formats_p = library.MagickQueryFormats(pattern_p, - ctypes.byref(number_formats)) - for cursor in range(number_formats.value): - value = ctypes.string_at(formats_p[cursor]) - formats.append(text(value)) - if formats_p: - formats_p = library.MagickRelinquishMemory(formats_p) - return formats - - -if __doc__ is not None: - __doc__ = __doc__.replace('0.0.0', VERSION) - -del libmagick - - -if __name__ == '__main__': # pragma: no cover - options = frozenset(sys.argv[1:]) - if '-v' in options or '--verbose' in options: - print('Wand', VERSION) - try: - print(MAGICK_VERSION) - except NameError: - pass - elif '--fonts' in options: - for font in fonts(): - print(font) - elif '--formats' in options: - for supported_format in formats(): - print(supported_format) - elif '--config' in options: - config_options = configure_options() - for key in config_options: - print('{:24s}: {}'.format(key, config_options[key])) - else: - print(VERSION) diff --git a/MangaJaNaiConverterGui/chaiNNer/python/Scripts/chardetect.exe b/MangaJaNaiConverterGui/chaiNNer/python/Scripts/chardetect.exe deleted file mode 100644 index b432e25..0000000 Binary files a/MangaJaNaiConverterGui/chaiNNer/python/Scripts/chardetect.exe and /dev/null differ diff --git a/SevenZipExtractor/ArchiveFile.cs b/SevenZipExtractor/ArchiveFile.cs deleted file mode 100644 index 96ffcd0..0000000 --- a/SevenZipExtractor/ArchiveFile.cs +++ /dev/null @@ -1,395 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; - -namespace SevenZipExtractor -{ - public class ArchiveFile : IDisposable - { - private SevenZipHandle sevenZipHandle; - private readonly IInArchive archive; - private readonly InStreamWrapper archiveStream; - private IList entries; - - private string libraryFilePath; - - public ArchiveFile(string archiveFilePath, string libraryFilePath = null) - { - this.libraryFilePath = libraryFilePath; - - this.InitializeAndValidateLibrary(); - - if (!File.Exists(archiveFilePath)) - { - throw new SevenZipException("Archive file not found"); - } - - SevenZipFormat format; - string extension = Path.GetExtension(archiveFilePath); - - if (this.GuessFormatFromExtension(extension, out format)) - { - // great - } - else if (this.GuessFormatFromSignature(archiveFilePath, out format)) - { - // success - } - else - { - throw new SevenZipException(Path.GetFileName(archiveFilePath) + " is not a known archive type"); - } - - this.archive = this.sevenZipHandle.CreateInArchive(Formats.FormatGuidMapping[format]); - this.archiveStream = new InStreamWrapper(File.OpenRead(archiveFilePath)); - } - - public ArchiveFile(Stream archiveStream, SevenZipFormat? format = null, string libraryFilePath = null) - { - this.libraryFilePath = libraryFilePath; - - this.InitializeAndValidateLibrary(); - - if (archiveStream == null) - { - throw new SevenZipException("archiveStream is null"); - } - - if (format == null) - { - SevenZipFormat guessedFormat; - - if (this.GuessFormatFromSignature(archiveStream, out guessedFormat)) - { - format = guessedFormat; - } - else - { - throw new SevenZipException("Unable to guess format automatically"); - } - } - - this.archive = this.sevenZipHandle.CreateInArchive(Formats.FormatGuidMapping[format.Value]); - this.archiveStream = new InStreamWrapper(archiveStream); - } - - public void Extract(string outputFolder, bool overwrite = false) - { - this.Extract(entry => - { - string fileName = Path.Combine(outputFolder, entry.FileName); - - if (entry.IsFolder) - { - return fileName; - } - - if (!File.Exists(fileName) || overwrite) - { - return fileName; - } - - return null; - }); - } - - public void Extract(Func getOutputPath) - { - IList fileStreams = new List(); - - try - { - foreach (Entry entry in Entries) - { - string outputPath = getOutputPath(entry); - - if (outputPath == null) // getOutputPath = null means SKIP - { - fileStreams.Add(null); - continue; - } - - if (entry.IsFolder) - { - Directory.CreateDirectory(outputPath); - fileStreams.Add(null); - continue; - } - - string directoryName = Path.GetDirectoryName(outputPath); - - if (!string.IsNullOrWhiteSpace(directoryName)) - { - Directory.CreateDirectory(directoryName); - } - - fileStreams.Add(File.Create(outputPath)); - } - - this.archive.Extract(null, 0xFFFFFFFF, 0, new ArchiveStreamsCallback(fileStreams)); - } - finally - { - foreach (Stream stream in fileStreams) - { - if (stream != null) - { - stream.Dispose(); - } - } - } - } - - public IList Entries - { - get - { - if (this.entries != null) - { - return this.entries; - } - - ulong checkPos = 32 * 1024; - int open = this.archive.Open(this.archiveStream, ref checkPos, null); - - if (open != 0) - { - throw new SevenZipException("Unable to open archive"); - } - - uint itemsCount = this.archive.GetNumberOfItems(); - - this.entries = new List(); - - for (uint fileIndex = 0; fileIndex < itemsCount; fileIndex++) - { - string fileName = this.GetProperty(fileIndex, ItemPropId.kpidPath); - bool isFolder = this.GetProperty(fileIndex, ItemPropId.kpidIsFolder); - bool isEncrypted = this.GetProperty(fileIndex, ItemPropId.kpidEncrypted); - ulong size = this.GetProperty(fileIndex, ItemPropId.kpidSize); - ulong packedSize = this.GetProperty(fileIndex, ItemPropId.kpidPackedSize); - DateTime creationTime = this.GetPropertySafe(fileIndex, ItemPropId.kpidCreationTime); - DateTime lastWriteTime = this.GetPropertySafe(fileIndex, ItemPropId.kpidLastWriteTime); - DateTime lastAccessTime = this.GetPropertySafe(fileIndex, ItemPropId.kpidLastAccessTime); - uint crc = this.GetPropertySafe(fileIndex, ItemPropId.kpidCRC); - uint attributes = this.GetPropertySafe(fileIndex, ItemPropId.kpidAttributes); - string comment = this.GetPropertySafe(fileIndex, ItemPropId.kpidComment); - string hostOS = this.GetPropertySafe(fileIndex, ItemPropId.kpidHostOS); - string method = this.GetPropertySafe(fileIndex, ItemPropId.kpidMethod); - - bool isSplitBefore = this.GetPropertySafe(fileIndex, ItemPropId.kpidSplitBefore); - bool isSplitAfter = this.GetPropertySafe(fileIndex, ItemPropId.kpidSplitAfter); - - this.entries.Add(new Entry(this.archive, fileIndex) - { - FileName = fileName, - IsFolder = isFolder, - IsEncrypted = isEncrypted, - Size = size, - PackedSize = packedSize, - CreationTime = creationTime, - LastWriteTime = lastWriteTime, - LastAccessTime = lastAccessTime, - CRC = crc, - Attributes = attributes, - Comment = comment, - HostOS = hostOS, - Method = method, - IsSplitBefore = isSplitBefore, - IsSplitAfter = isSplitAfter - }); - } - - return this.entries; - } - } - - private T GetPropertySafe(uint fileIndex, ItemPropId name) - { - try - { - return this.GetProperty(fileIndex, name); - } - catch (InvalidCastException) - { - return default(T); - } - } - - private T GetProperty(uint fileIndex, ItemPropId name) - { - PropVariant propVariant = new PropVariant(); - this.archive.GetProperty(fileIndex, name, ref propVariant); - object value = propVariant.GetObject(); - - if (propVariant.VarType == VarEnum.VT_EMPTY) - { - propVariant.Clear(); - return default(T); - } - - propVariant.Clear(); - - if (value == null) - { - return default(T); - } - - Type type = typeof(T); - bool isNullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - Type underlyingType = isNullable ? Nullable.GetUnderlyingType(type) : type; - - T result = (T)Convert.ChangeType(value.ToString(), underlyingType); - - return result; - } - - private void InitializeAndValidateLibrary() - { - if (string.IsNullOrWhiteSpace(this.libraryFilePath)) - { - string currentArchitecture = IntPtr.Size == 4 ? "x86" : "x64"; // magic check - - if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7z-" + currentArchitecture + ".dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "7z-" + currentArchitecture + ".dll"); - } - else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "7z-" + currentArchitecture + ".dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", "7z-" + currentArchitecture + ".dll"); - } - else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", currentArchitecture, "7z.dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin", currentArchitecture, "7z.dll"); - } - else if (File.Exists(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, currentArchitecture, "7z.dll"))) - { - this.libraryFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, currentArchitecture, "7z.dll"); - } - else if (File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.dll"))) - { - this.libraryFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "7-Zip", "7z.dll"); - } - } - - if (string.IsNullOrWhiteSpace(this.libraryFilePath)) - { - throw new SevenZipException("libraryFilePath not set"); - } - - if (!File.Exists(this.libraryFilePath)) - { - throw new SevenZipException("7z.dll not found"); - } - - try - { - this.sevenZipHandle = new SevenZipHandle(this.libraryFilePath); - } - catch (Exception e) - { - throw new SevenZipException("Unable to initialize SevenZipHandle", e); - } - } - - private bool GuessFormatFromExtension(string fileExtension, out SevenZipFormat format) - { - if (string.IsNullOrWhiteSpace(fileExtension)) - { - format = SevenZipFormat.Undefined; - return false; - } - - fileExtension = fileExtension.TrimStart('.').Trim().ToLowerInvariant(); - - if (fileExtension.Equals("rar")) - { - // 7z has different GUID for Pre-RAR5 and RAR5, but they have both same extension (.rar) - // If it is [0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00] then file is RAR5 otherwise RAR. - // https://www.rarlab.com/technote.htm - - // We are unable to guess right format just by looking at extension and have to check signature - - format = SevenZipFormat.Undefined; - return false; - } - - if (!Formats.ExtensionFormatMapping.ContainsKey(fileExtension)) - { - format = SevenZipFormat.Undefined; - return false; - } - - format = Formats.ExtensionFormatMapping[fileExtension]; - return true; - } - - - private bool GuessFormatFromSignature(string filePath, out SevenZipFormat format) - { - using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - return GuessFormatFromSignature(fileStream, out format); - } - } - - private bool GuessFormatFromSignature(Stream stream, out SevenZipFormat format) - { - int longestSignature = Formats.FileSignatures.Values.OrderByDescending(v => v.Length).First().Length; - - byte[] archiveFileSignature = new byte[longestSignature]; - int bytesRead = stream.Read(archiveFileSignature, 0, longestSignature); - - stream.Position -= bytesRead; // go back o beginning - - if (bytesRead != longestSignature) - { - format = SevenZipFormat.Undefined; - return false; - } - - foreach (KeyValuePair pair in Formats.FileSignatures) - { - if (archiveFileSignature.Take(pair.Value.Length).SequenceEqual(pair.Value)) - { - format = pair.Key; - return true; - } - } - - format = SevenZipFormat.Undefined; - return false; - } - - ~ArchiveFile() - { - this.Dispose(false); - } - - protected void Dispose(bool disposing) - { - if (this.archiveStream != null) - { - this.archiveStream.Dispose(); - } - - if (this.archive != null) - { - Marshal.ReleaseComObject(this.archive); - } - - if (this.sevenZipHandle != null) - { - this.sevenZipHandle.Dispose(); - } - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - } -} diff --git a/SevenZipExtractor/ArchiveFileCallback.cs b/SevenZipExtractor/ArchiveFileCallback.cs deleted file mode 100644 index e86cc63..0000000 --- a/SevenZipExtractor/ArchiveFileCallback.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.IO; - -namespace SevenZipExtractor -{ - internal class ArchiveFileCallback : IArchiveExtractCallback - { - private readonly string fileName; - private readonly uint fileNumber; - private OutStreamWrapper fileStream; // to be removed - - public ArchiveFileCallback(uint fileNumber, string fileName) - { - this.fileNumber = fileNumber; - this.fileName = fileName; - } - - public void SetTotal(ulong total) - { - } - - public void SetCompleted(ref ulong completeValue) - { - } - - public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) - { - if ((index != this.fileNumber) || (askExtractMode != AskMode.kExtract)) - { - outStream = null; - return 0; - } - - string fileDir = Path.GetDirectoryName(this.fileName); - - if (!string.IsNullOrEmpty(fileDir)) - { - Directory.CreateDirectory(fileDir); - } - - this.fileStream = new OutStreamWrapper(File.Create(this.fileName)); - - outStream = this.fileStream; - - return 0; - } - - public void PrepareOperation(AskMode askExtractMode) - { - } - - public void SetOperationResult(OperationResult resultEOperationResult) - { - this.fileStream.Dispose(); - } - } -} \ No newline at end of file diff --git a/SevenZipExtractor/ArchiveStreamCallback.cs b/SevenZipExtractor/ArchiveStreamCallback.cs deleted file mode 100644 index 741e57a..0000000 --- a/SevenZipExtractor/ArchiveStreamCallback.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.IO; - -namespace SevenZipExtractor -{ - internal class ArchiveStreamCallback : IArchiveExtractCallback - { - private readonly uint fileNumber; - private readonly Stream stream; - - public ArchiveStreamCallback(uint fileNumber, Stream stream) - { - this.fileNumber = fileNumber; - this.stream = stream; - } - - public void SetTotal(ulong total) - { - } - - public void SetCompleted(ref ulong completeValue) - { - } - - public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) - { - if ((index != this.fileNumber) || (askExtractMode != AskMode.kExtract)) - { - outStream = null; - return 0; - } - - outStream = new OutStreamWrapper(this.stream); - - return 0; - } - - public void PrepareOperation(AskMode askExtractMode) - { - } - - public void SetOperationResult(OperationResult resultEOperationResult) - { - } - } -} \ No newline at end of file diff --git a/SevenZipExtractor/ArchiveStreamsCallback.cs b/SevenZipExtractor/ArchiveStreamsCallback.cs deleted file mode 100644 index b8f1fca..0000000 --- a/SevenZipExtractor/ArchiveStreamsCallback.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace SevenZipExtractor -{ - internal class ArchiveStreamsCallback : IArchiveExtractCallback - { - private readonly IList streams; - - public ArchiveStreamsCallback(IList streams) - { - this.streams = streams; - } - - public void SetTotal(ulong total) - { - } - - public void SetCompleted(ref ulong completeValue) - { - } - - public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) - { - if (askExtractMode != AskMode.kExtract) - { - outStream = null; - return 0; - } - - if (this.streams == null) - { - outStream = null; - return 0; - } - - Stream stream = this.streams[(int) index]; - - if (stream == null) - { - outStream = null; - return 0; - } - - outStream = new OutStreamWrapper(stream); - - return 0; - } - - public void PrepareOperation(AskMode askExtractMode) - { - } - - public void SetOperationResult(OperationResult resultEOperationResult) - { - } - } -} \ No newline at end of file diff --git a/SevenZipExtractor/Entry.cs b/SevenZipExtractor/Entry.cs deleted file mode 100644 index 201683b..0000000 --- a/SevenZipExtractor/Entry.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.IO; - -namespace SevenZipExtractor -{ - public class Entry - { - private readonly IInArchive archive; - private readonly uint index; - - internal Entry(IInArchive archive, uint index) - { - this.archive = archive; - this.index = index; - } - - /// - /// Name of the file with its relative path within the archive - /// - public string FileName { get; internal set; } - /// - /// True if entry is a folder, false if it is a file - /// - public bool IsFolder { get; internal set; } - /// - /// Original entry size - /// - public ulong Size { get; internal set; } - /// - /// Entry size in a archived state - /// - public ulong PackedSize { get; internal set; } - - /// - /// Date and time of the file (entry) creation - /// - public DateTime CreationTime { get; internal set; } - - /// - /// Date and time of the last change of the file (entry) - /// - public DateTime LastWriteTime { get; internal set; } - - /// - /// Date and time of the last access of the file (entry) - /// - public DateTime LastAccessTime { get; internal set; } - - /// - /// CRC hash of the entry - /// - public UInt32 CRC { get; internal set; } - - /// - /// Attributes of the entry - /// - public UInt32 Attributes { get; internal set; } - - /// - /// True if entry is encrypted, otherwise false - /// - public bool IsEncrypted { get; internal set; } - - /// - /// Comment of the entry - /// - public string Comment { get; internal set; } - - /// - /// Compression method of the entry - /// - public string Method { get; internal set; } - - /// - /// Host operating system of the entry - /// - public string HostOS { get; internal set; } - - /// - /// True if there are parts of this file in previous split archive parts - /// - public bool IsSplitBefore { get; set; } - - /// - /// True if there are parts of this file in next split archive parts - /// - public bool IsSplitAfter { get; set; } - - public void Extract(string fileName, bool preserveTimestamp = true) - { - if (this.IsFolder) - { - Directory.CreateDirectory(fileName); - return; - } - - string directoryName = Path.GetDirectoryName(fileName); - - if (!string.IsNullOrWhiteSpace(directoryName)) - { - Directory.CreateDirectory(directoryName); - } - - using (FileStream fileStream = File.Create(fileName)) - { - this.Extract(fileStream); - } - - if (preserveTimestamp) - { - File.SetLastWriteTime(fileName, this.LastWriteTime); - } - } - public void Extract(Stream stream) - { - this.archive.Extract(new[] { this.index }, 1, 0, new ArchiveStreamCallback(this.index, stream)); - } - } -} diff --git a/SevenZipExtractor/Formats.cs b/SevenZipExtractor/Formats.cs deleted file mode 100644 index 5c190d2..0000000 --- a/SevenZipExtractor/Formats.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace SevenZipExtractor -{ - public class Formats - { - internal static readonly Dictionary ExtensionFormatMapping = new Dictionary - { - {"7z", SevenZipFormat.SevenZip}, - {"gz", SevenZipFormat.GZip}, - {"tar", SevenZipFormat.Tar}, - {"rar", SevenZipFormat.Rar}, - {"zip", SevenZipFormat.Zip}, - {"lzma", SevenZipFormat.Lzma}, - {"lzh", SevenZipFormat.Lzh}, - {"arj", SevenZipFormat.Arj}, - {"bz2", SevenZipFormat.BZip2}, - {"cab", SevenZipFormat.Cab}, - {"chm", SevenZipFormat.Chm}, - {"deb", SevenZipFormat.Deb}, - {"iso", SevenZipFormat.Iso}, - {"rpm", SevenZipFormat.Rpm}, - {"wim", SevenZipFormat.Wim}, - {"udf", SevenZipFormat.Udf}, - {"mub", SevenZipFormat.Mub}, - {"xar", SevenZipFormat.Xar}, - {"hfs", SevenZipFormat.Hfs}, - {"dmg", SevenZipFormat.Dmg}, - {"z", SevenZipFormat.Lzw}, - {"xz", SevenZipFormat.XZ}, - {"flv", SevenZipFormat.Flv}, - {"swf", SevenZipFormat.Swf}, - {"exe", SevenZipFormat.PE}, - {"dll", SevenZipFormat.PE}, - {"vhd", SevenZipFormat.Vhd} - }; - - internal static Dictionary FormatGuidMapping = new Dictionary - { - {SevenZipFormat.SevenZip, new Guid("23170f69-40c1-278a-1000-000110070000")}, - {SevenZipFormat.Arj, new Guid("23170f69-40c1-278a-1000-000110040000")}, - {SevenZipFormat.BZip2, new Guid("23170f69-40c1-278a-1000-000110020000")}, - {SevenZipFormat.Cab, new Guid("23170f69-40c1-278a-1000-000110080000")}, - {SevenZipFormat.Chm, new Guid("23170f69-40c1-278a-1000-000110e90000")}, - {SevenZipFormat.Compound, new Guid("23170f69-40c1-278a-1000-000110e50000")}, - {SevenZipFormat.Cpio, new Guid("23170f69-40c1-278a-1000-000110ed0000")}, - {SevenZipFormat.Deb, new Guid("23170f69-40c1-278a-1000-000110ec0000")}, - {SevenZipFormat.GZip, new Guid("23170f69-40c1-278a-1000-000110ef0000")}, - {SevenZipFormat.Iso, new Guid("23170f69-40c1-278a-1000-000110e70000")}, - {SevenZipFormat.Lzh, new Guid("23170f69-40c1-278a-1000-000110060000")}, - {SevenZipFormat.Lzma, new Guid("23170f69-40c1-278a-1000-0001100a0000")}, - {SevenZipFormat.Nsis, new Guid("23170f69-40c1-278a-1000-000110090000")}, - {SevenZipFormat.Rar, new Guid("23170f69-40c1-278a-1000-000110030000")}, - {SevenZipFormat.Rar5, new Guid("23170f69-40c1-278a-1000-000110CC0000")}, - {SevenZipFormat.Rpm, new Guid("23170f69-40c1-278a-1000-000110eb0000")}, - {SevenZipFormat.Split, new Guid("23170f69-40c1-278a-1000-000110ea0000")}, - {SevenZipFormat.Tar, new Guid("23170f69-40c1-278a-1000-000110ee0000")}, - {SevenZipFormat.Wim, new Guid("23170f69-40c1-278a-1000-000110e60000")}, - {SevenZipFormat.Lzw, new Guid("23170f69-40c1-278a-1000-000110050000")}, - {SevenZipFormat.Zip, new Guid("23170f69-40c1-278a-1000-000110010000")}, - {SevenZipFormat.Udf, new Guid("23170f69-40c1-278a-1000-000110E00000")}, - {SevenZipFormat.Xar, new Guid("23170f69-40c1-278a-1000-000110E10000")}, - {SevenZipFormat.Mub, new Guid("23170f69-40c1-278a-1000-000110E20000")}, - {SevenZipFormat.Hfs, new Guid("23170f69-40c1-278a-1000-000110E30000")}, - {SevenZipFormat.Dmg, new Guid("23170f69-40c1-278a-1000-000110E40000")}, - {SevenZipFormat.XZ, new Guid("23170f69-40c1-278a-1000-0001100C0000")}, - {SevenZipFormat.Mslz, new Guid("23170f69-40c1-278a-1000-000110D50000")}, - {SevenZipFormat.PE, new Guid("23170f69-40c1-278a-1000-000110DD0000")}, - {SevenZipFormat.Elf, new Guid("23170f69-40c1-278a-1000-000110DE0000")}, - {SevenZipFormat.Swf, new Guid("23170f69-40c1-278a-1000-000110D70000")}, - {SevenZipFormat.Vhd, new Guid("23170f69-40c1-278a-1000-000110DC0000")}, - {SevenZipFormat.Flv, new Guid("23170f69-40c1-278a-1000-000110D60000")}, - {SevenZipFormat.SquashFS, new Guid("23170f69-40c1-278a-1000-000110D20000")}, - {SevenZipFormat.Lzma86, new Guid("23170f69-40c1-278a-1000-0001100B0000")}, - {SevenZipFormat.Ppmd, new Guid("23170f69-40c1-278a-1000-0001100D0000")}, - {SevenZipFormat.TE, new Guid("23170f69-40c1-278a-1000-000110CF0000")}, - {SevenZipFormat.UEFIc, new Guid("23170f69-40c1-278a-1000-000110D00000")}, - {SevenZipFormat.UEFIs, new Guid("23170f69-40c1-278a-1000-000110D10000")}, - {SevenZipFormat.CramFS, new Guid("23170f69-40c1-278a-1000-000110D30000")}, - {SevenZipFormat.APM, new Guid("23170f69-40c1-278a-1000-000110D40000")}, - {SevenZipFormat.Swfc, new Guid("23170f69-40c1-278a-1000-000110D80000")}, - {SevenZipFormat.Ntfs, new Guid("23170f69-40c1-278a-1000-000110D90000")}, - {SevenZipFormat.Fat, new Guid("23170f69-40c1-278a-1000-000110DA0000")}, - {SevenZipFormat.Mbr, new Guid("23170f69-40c1-278a-1000-000110DB0000")}, - {SevenZipFormat.MachO, new Guid("23170f69-40c1-278a-1000-000110DF0000")} - }; - - internal static Dictionary FileSignatures = new Dictionary - { - {SevenZipFormat.Rar5, new byte[] {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00}}, - {SevenZipFormat.Rar, new byte[] { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 }}, - {SevenZipFormat.Vhd, new byte[] { 0x63, 0x6F, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x78 }}, - {SevenZipFormat.Deb, new byte[] { 0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E }}, - {SevenZipFormat.Dmg, new byte[] { 0x78, 0x01, 0x73, 0x0D, 0x62, 0x62, 0x60 }}, - {SevenZipFormat.SevenZip, new byte[] { 0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C }}, - {SevenZipFormat.Tar, new byte[] { 0x75, 0x73, 0x74, 0x61, 0x72 }}, - {SevenZipFormat.Iso, new byte[] { 0x43, 0x44, 0x30, 0x30, 0x31 }}, - {SevenZipFormat.Cab, new byte[] { 0x4D, 0x53, 0x43, 0x46 }}, - {SevenZipFormat.Rpm, new byte[] { 0xed, 0xab, 0xee, 0xdb }}, - {SevenZipFormat.Xar, new byte[] { 0x78, 0x61, 0x72, 0x21 }}, - {SevenZipFormat.Chm, new byte[] { 0x49, 0x54, 0x53, 0x46 }}, - {SevenZipFormat.BZip2, new byte[] { 0x42, 0x5A, 0x68 }}, - {SevenZipFormat.Flv, new byte[] { 0x46, 0x4C, 0x56 }}, - {SevenZipFormat.Swf, new byte[] { 0x46, 0x57, 0x53 }}, - {SevenZipFormat.GZip, new byte[] { 0x1f, 0x0b }}, - {SevenZipFormat.Zip, new byte[] { 0x50, 0x4b }}, - {SevenZipFormat.Arj, new byte[] { 0x60, 0xEA }}, - {SevenZipFormat.Lzh, new byte[] { 0x2D, 0x6C, 0x68 }}, - {SevenZipFormat.SquashFS, new byte[] {0x68, 0x73, 0x71, 0x73}} - }; - } -} diff --git a/SevenZipExtractor/IArchiveExtractCallback.cs b/SevenZipExtractor/IArchiveExtractCallback.cs deleted file mode 100644 index 9493835..0000000 --- a/SevenZipExtractor/IArchiveExtractCallback.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Runtime.InteropServices; - -namespace SevenZipExtractor -{ - [ComImport] - [Guid("23170F69-40C1-278A-0000-000600200000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IArchiveExtractCallback //: IProgress - { - void SetTotal(ulong total); - void SetCompleted([In] ref ulong completeValue); - - [PreserveSig] - int GetStream( - uint index, - [MarshalAs(UnmanagedType.Interface)] out ISequentialOutStream outStream, - AskMode askExtractMode); - // GetStream OUT: S_OK - OK, S_FALSE - skeep this file - - void PrepareOperation(AskMode askExtractMode); - void SetOperationResult(OperationResult resultEOperationResult); - } -} \ No newline at end of file diff --git a/SevenZipExtractor/Kernel32Dll.cs b/SevenZipExtractor/Kernel32Dll.cs deleted file mode 100644 index c69a88a..0000000 --- a/SevenZipExtractor/Kernel32Dll.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using System.Security; - -namespace SevenZipExtractor -{ - internal static class Kernel32Dll - { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - internal static extern SafeLibraryHandle LoadLibrary([MarshalAs(UnmanagedType.LPTStr)] string lpFileName); - - [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern IntPtr GetProcAddress(SafeLibraryHandle hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); - - [SuppressUnmanagedCodeSecurity] - [DllImport("kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool FreeLibrary(IntPtr hModule); - } -} \ No newline at end of file diff --git a/SevenZipExtractor/SafeLibraryHandle.cs b/SevenZipExtractor/SafeLibraryHandle.cs deleted file mode 100644 index ec80661..0000000 --- a/SevenZipExtractor/SafeLibraryHandle.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Runtime.ConstrainedExecution; -using Microsoft.Win32.SafeHandles; - -namespace SevenZipExtractor -{ - internal sealed class SafeLibraryHandle : SafeHandleZeroOrMinusOneIsInvalid - { - public SafeLibraryHandle() : base(true) - { - } - - /// Release library handle - /// true if the handle was released - protected override bool ReleaseHandle() - { - return Kernel32Dll.FreeLibrary(this.handle); - } - } -} \ No newline at end of file diff --git a/SevenZipExtractor/SevenZipException.cs b/SevenZipExtractor/SevenZipException.cs deleted file mode 100644 index b4f6676..0000000 --- a/SevenZipExtractor/SevenZipException.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace SevenZipExtractor -{ - public class SevenZipException : Exception - { - public SevenZipException() - { - } - - public SevenZipException(string message) : base(message) - { - } - - public SevenZipException(string message, Exception innerException) : base(message, innerException) - { - } - - protected SevenZipException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } -} \ No newline at end of file diff --git a/SevenZipExtractor/SevenZipExtractor.csproj b/SevenZipExtractor/SevenZipExtractor.csproj deleted file mode 100644 index 60dddb3..0000000 --- a/SevenZipExtractor/SevenZipExtractor.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - net8.0 - SevenZipWrapper - SevenZipWrapper - C# wrapper for 7z.dll (included) - Copyright 2022 - 1.0.16 - bin\$(Configuration)\ - Eugene Sichkar, Alexander Selishchev, @matortheeternal, Hajin Jang, Artem Tarasov, Jose Pineiro, Raphael Stoeckli, Wojciech Nagórski - SevenZipExtractor - https://github.com/adoconnection/SevenZipExtractor - 7Zip APM Arj BZip2 Cab Chm Compound Cpio CramFS Deb Dll Dmg Exe Fat Flv GZip Hfs Iso Lzh Lzma Lzma86 Mach-O Mbr Mub Nsis Ntfs Ppmd Rar Rar5 Rpm Split SquashFS Swf Swfc Tar TE Udf UEFIc UEFIs Vhd Wim Xar XZ Z Zip - 1.0.17 - - - - build\x64\ - PreserveNewest - true - - - build\x86\ - PreserveNewest - true - - - Never - - - \ No newline at end of file diff --git a/SevenZipExtractor/SevenZipExtractor.targets b/SevenZipExtractor/SevenZipExtractor.targets deleted file mode 100644 index 186a714..0000000 --- a/SevenZipExtractor/SevenZipExtractor.targets +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - %(RecursiveDir)%(FileName)%(Extension) - PreserveNewest - - - \ No newline at end of file diff --git a/SevenZipExtractor/SevenZipFormat.cs b/SevenZipExtractor/SevenZipFormat.cs deleted file mode 100644 index c95eae5..0000000 --- a/SevenZipExtractor/SevenZipFormat.cs +++ /dev/null @@ -1,285 +0,0 @@ -namespace SevenZipExtractor -{ - /// - /// - /// - public enum SevenZipFormat - { - // Default invalid format value - Undefined = 0, - - /// - /// Open 7-zip archive format. - /// - /// Wikipedia information - SevenZip, - - /// - /// Proprietary Arj archive format. - /// - /// Wikipedia information - Arj, - - /// - /// Open Bzip2 archive format. - /// - /// Wikipedia information - BZip2, - - /// - /// Microsoft cabinet archive format. - /// - /// Wikipedia information - Cab, - - /// - /// Microsoft Compiled HTML Help file format. - /// - /// Wikipedia information - Chm, - - /// - /// Microsoft Compound file format. - /// - /// Wikipedia information - Compound, - - /// - /// Open Cpio archive format. - /// - /// Wikipedia information - Cpio, - - /// - /// Open Debian software package format. - /// - /// Wikipedia information - Deb, - - /// - /// Open Gzip archive format. - /// - /// Wikipedia information - GZip, - - /// - /// Open ISO disk image format. - /// - /// Wikipedia information - Iso, - - /// - /// Open Lzh archive format. - /// - /// Wikipedia information - Lzh, - - /// - /// Open core 7-zip Lzma raw archive format. - /// - /// Wikipedia information - Lzma, - - /// - /// Nullsoft installation package format. - /// - /// Wikipedia information - Nsis, - - /// - /// RarLab Rar archive format. - /// - /// Wikipedia information - Rar, - - /// - /// RarLab Rar archive format, version 5. - /// - /// Wikipedia information - Rar5, - - /// - /// Open Rpm software package format. - /// - /// Wikipedia information - Rpm, - - /// - /// Open split file format. - /// - /// Wikipedia information - Split, - - /// - /// Open Tar archive format. - /// - /// Wikipedia information - Tar, - - /// - /// Microsoft Windows Imaging disk image format. - /// - /// Wikipedia information - Wim, - - /// - /// Open LZW archive format; implemented in "compress" program; also known as "Z" archive format. - /// - /// Wikipedia information - Lzw, - - /// - /// Open Zip archive format. - /// - /// Wikipedia information - Zip, - - /// - /// Open Udf disk image format. - /// - Udf, - - /// - /// Xar open source archive format. - /// - /// Wikipedia information - Xar, - - /// - /// Mub - /// - Mub, - - /// - /// Macintosh Disk Image on CD. - /// - /// Wikipedia information - Hfs, - - /// - /// Apple Mac OS X Disk Copy Disk Image format. - /// - Dmg, - - /// - /// Open Xz archive format. - /// - /// Wikipedia information - XZ, - - /// - /// MSLZ archive format. - /// - Mslz, - - /// - /// Flash video format. - /// - /// Wikipedia information - Flv, - - /// - /// Shockwave Flash format. - /// - /// Wikipedia information - Swf, - - /// - /// Windows PE executable format. - /// - /// Wikipedia information - PE, - - /// - /// Linux executable Elf format. - /// - /// Wikipedia information - Elf, - - /// - /// Windows Installer Database. - /// - /// Wikipedia information - Msi, - - /// - /// Microsoft virtual hard disk file format. - /// - /// Wikipedia information - Vhd, - - /// - /// SquashFS file system format. - /// - /// Wikipedia information - SquashFS, - - /// - /// Lzma86 file format. - /// - Lzma86, - - /// - /// Prediction by Partial Matching by Dmitry algorithm. - /// - /// Wikipedia information - Ppmd, - - /// - /// TE format. - /// - TE, - - /// - /// UEFIc format. - /// - /// Wikipedia information - UEFIc, - - /// - /// UEFIs format. - /// - /// Wikipedia information - UEFIs, - - /// - /// Compressed ROM file system format. - /// - /// Wikipedia information - CramFS, - - /// - /// APM format. - /// - APM, - - /// - /// Swfc format. - /// - Swfc, - - /// - /// NTFS file system format. - /// - /// Wikipedia information - Ntfs, - - /// - /// FAT file system format. - /// - /// Wikipedia information - Fat, - - /// - /// MBR format. - /// - /// Wikipedia information - Mbr, - - /// - /// Mach-O file format. - /// - /// Wikipedia information - MachO - } -} \ No newline at end of file diff --git a/SevenZipExtractor/SevenZipHandle.cs b/SevenZipExtractor/SevenZipHandle.cs deleted file mode 100644 index 8fa45fb..0000000 --- a/SevenZipExtractor/SevenZipHandle.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; - -namespace SevenZipExtractor -{ - internal class SevenZipHandle : IDisposable - { - private SafeLibraryHandle sevenZipSafeHandle; - - public SevenZipHandle(string sevenZipLibPath) - { - this.sevenZipSafeHandle = Kernel32Dll.LoadLibrary(sevenZipLibPath); - - if (this.sevenZipSafeHandle.IsInvalid) - { - throw new Win32Exception(); - } - - IntPtr functionPtr = Kernel32Dll.GetProcAddress(this.sevenZipSafeHandle, "GetHandlerProperty"); - - // Not valid dll - if (functionPtr == IntPtr.Zero) - { - this.sevenZipSafeHandle.Close(); - throw new ArgumentException(); - } - } - - ~SevenZipHandle() - { - this.Dispose(false); - } - - protected void Dispose(bool disposing) - { - if ((this.sevenZipSafeHandle != null) && !this.sevenZipSafeHandle.IsClosed) - { - this.sevenZipSafeHandle.Close(); - } - - this.sevenZipSafeHandle = null; - } - - public void Dispose() - { - this.Dispose(true); - GC.SuppressFinalize(this); - } - - public IInArchive CreateInArchive(Guid classId) - { - if (this.sevenZipSafeHandle == null) - { - throw new ObjectDisposedException("SevenZipHandle"); - } - - IntPtr procAddress = Kernel32Dll.GetProcAddress(this.sevenZipSafeHandle, "CreateObject"); - CreateObjectDelegate createObject = (CreateObjectDelegate) Marshal.GetDelegateForFunctionPointer(procAddress, typeof (CreateObjectDelegate)); - - object result; - Guid interfaceId = typeof (IInArchive).GUID; - createObject(ref classId, ref interfaceId, out result); - - return result as IInArchive; - } - } -} \ No newline at end of file diff --git a/SevenZipExtractor/SevenZipInterface.cs b/SevenZipExtractor/SevenZipInterface.cs deleted file mode 100644 index cf73b5c..0000000 --- a/SevenZipExtractor/SevenZipInterface.cs +++ /dev/null @@ -1,459 +0,0 @@ -// Version 1.5 - -using System; -using System.Globalization; -using System.IO; -using System.Runtime.InteropServices; -using System.Security.Permissions; -using System.Threading; - -namespace SevenZipExtractor -{ - [StructLayout(LayoutKind.Sequential)] - internal struct PropArray - { - uint length; - IntPtr pointerValues; - } - - [StructLayout(LayoutKind.Explicit)] - internal struct PropVariant - { - [DllImport("ole32.dll")] - private static extern int PropVariantClear(ref PropVariant pvar); - - [FieldOffset(0)] public ushort vt; - [FieldOffset(8)] public IntPtr pointerValue; - [FieldOffset(8)] public byte byteValue; - [FieldOffset(8)] public long longValue; - [FieldOffset(8)] public System.Runtime.InteropServices.ComTypes.FILETIME filetime; - [FieldOffset(8)] public PropArray propArray; - - public VarEnum VarType - { - get - { - return (VarEnum) this.vt; - } - } - - public void Clear() - { - switch (this.VarType) - { - case VarEnum.VT_EMPTY: - break; - - case VarEnum.VT_NULL: - case VarEnum.VT_I2: - case VarEnum.VT_I4: - case VarEnum.VT_R4: - case VarEnum.VT_R8: - case VarEnum.VT_CY: - case VarEnum.VT_DATE: - case VarEnum.VT_ERROR: - case VarEnum.VT_BOOL: - //case VarEnum.VT_DECIMAL: - case VarEnum.VT_I1: - case VarEnum.VT_UI1: - case VarEnum.VT_UI2: - case VarEnum.VT_UI4: - case VarEnum.VT_I8: - case VarEnum.VT_UI8: - case VarEnum.VT_INT: - case VarEnum.VT_UINT: - case VarEnum.VT_HRESULT: - case VarEnum.VT_FILETIME: - this.vt = 0; - break; - - default: - PropVariantClear(ref this); - break; - } - } - - public object GetObject() - { - switch (this.VarType) - { - case VarEnum.VT_EMPTY: - return null; - - case VarEnum.VT_FILETIME: - return DateTime.FromFileTime(this.longValue); - - default: - GCHandle PropHandle = GCHandle.Alloc(this, GCHandleType.Pinned); - - try - { - return Marshal.GetObjectForNativeVariant(PropHandle.AddrOfPinnedObject()); - } - finally - { - PropHandle.Free(); - } - } - } - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000000050000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IProgress - { - void SetTotal(ulong total); - void SetCompleted([In] ref ulong completeValue); - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000600100000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IArchiveOpenCallback - { - // ref ulong replaced with IntPtr because handlers ofter pass null value - // read actual value with Marshal.ReadInt64 - void SetTotal( - IntPtr files, // [In] ref ulong files, can use 'ulong* files' but it is unsafe - IntPtr bytes); // [In] ref ulong bytes - - void SetCompleted( - IntPtr files, // [In] ref ulong files - IntPtr bytes); // [In] ref ulong bytes - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000500100000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface ICryptoGetTextPassword - { - [PreserveSig] - int CryptoGetTextPassword( - [MarshalAs(UnmanagedType.BStr)] out string password); - - //[return : MarshalAs(UnmanagedType.BStr)] - //string CryptoGetTextPassword(); - } - - internal enum AskMode : int - { - kExtract = 0, - kTest, - kSkip - } - - internal enum OperationResult : int - { - kOK = 0, - kUnSupportedMethod, - kDataError, - kCRCError - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000600300000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IArchiveOpenVolumeCallback - { - void GetProperty( - ItemPropId propID, // PROPID - IntPtr value); // PROPVARIANT - - [PreserveSig] - int GetStream( - [MarshalAs(UnmanagedType.LPWStr)] string name, - [MarshalAs(UnmanagedType.Interface)] out IInStream inStream); - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000600400000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IInArchiveGetStream - { - [return: MarshalAs(UnmanagedType.Interface)] - ISequentialInStream GetStream(uint index); - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000300010000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface ISequentialInStream - { - //[PreserveSig] - //int Read( - // [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, - // uint size, - // IntPtr processedSize); // ref uint processedSize - - uint Read( - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, - uint size); - - /* - Out: if size != 0, return_value = S_OK and (*processedSize == 0), - then there are no more bytes in stream. - if (size > 0) && there are bytes in stream, - this function must read at least 1 byte. - This function is allowed to read less than number of remaining bytes in stream. - You must call Read function in loop, if you need exact amount of data - */ - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000300020000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface ISequentialOutStream - { - [PreserveSig] - int Write( - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, - uint size, - IntPtr processedSize); // ref uint processedSize - /* - if (size > 0) this function must write at least 1 byte. - This function is allowed to write less than "size". - You must call Write function in loop, if you need to write exact amount of data - */ - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000300030000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IInStream //: ISequentialInStream - { - //[PreserveSig] - //int Read( - // [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, - // uint size, - // IntPtr processedSize); // ref uint processedSize - - uint Read( - [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, - uint size); - - //[PreserveSig] - void Seek( - long offset, - uint seekOrigin, - IntPtr newPosition); // ref long newPosition - } - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000300040000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IOutStream //: ISequentialOutStream - { - [PreserveSig] - int Write( - [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] data, - uint size, - IntPtr processedSize); // ref uint processedSize - - //[PreserveSig] - void Seek( - long offset, - uint seekOrigin, - IntPtr newPosition); // ref long newPosition - - [PreserveSig] - int SetSize(long newSize); - } - - internal enum ItemPropId : uint - { - kpidNoProperty = 0, - - kpidHandlerItemIndex = 2, - kpidPath, - kpidName, - kpidExtension, - kpidIsFolder, - kpidSize, - kpidPackedSize, - kpidAttributes, - kpidCreationTime, - kpidLastAccessTime, - kpidLastWriteTime, - kpidSolid, - kpidCommented, - kpidEncrypted, - kpidSplitBefore, - kpidSplitAfter, - kpidDictionarySize, - kpidCRC, - kpidType, - kpidIsAnti, - kpidMethod, - kpidHostOS, - kpidFileSystem, - kpidUser, - kpidGroup, - kpidBlock, - kpidComment, - kpidPosition, - kpidPrefix, - - kpidTotalSize = 0x1100, - kpidFreeSpace, - kpidClusterSize, - kpidVolumeName, - - kpidLocalName = 0x1200, - kpidProvider, - - kpidUserDefined = 0x10000 - } - - - [ComImport] - [Guid("23170F69-40C1-278A-0000-000600600000")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - //[AutomationProxy(true)] - internal interface IInArchive - { - [PreserveSig] - int Open( - IInStream stream, - /*[MarshalAs(UnmanagedType.U8)]*/ [In] ref ulong maxCheckStartPosition, - [MarshalAs(UnmanagedType.Interface)] IArchiveOpenCallback openArchiveCallback); - - void Close(); - //void GetNumberOfItems([In] ref uint numItem); - uint GetNumberOfItems(); - - void GetProperty( - uint index, - ItemPropId propID, // PROPID - ref PropVariant value); // PROPVARIANT - - [PreserveSig] - int Extract( - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] uint[] indices, //[In] ref uint indices, - uint numItems, - int testMode, - [MarshalAs(UnmanagedType.Interface)] IArchiveExtractCallback extractCallback); - - // indices must be sorted - // numItems = 0xFFFFFFFF means all files - // testMode != 0 means "test files operation" - - void GetArchiveProperty( - uint propID, // PROPID - ref PropVariant value); // PROPVARIANT - - //void GetNumberOfProperties([In] ref uint numProperties); - uint GetNumberOfProperties(); - - void GetPropertyInfo( - uint index, - [MarshalAs(UnmanagedType.BStr)] out string name, - out ItemPropId propID, // PROPID - out ushort varType); //VARTYPE - - //void GetNumberOfArchiveProperties([In] ref uint numProperties); - uint GetNumberOfArchiveProperties(); - - void GetArchivePropertyInfo( - uint index, - [MarshalAs(UnmanagedType.BStr)] string name, - ref uint propID, // PROPID - ref ushort varType); //VARTYPE - } - - internal enum ArchivePropId : uint - { - kName = 0, - kClassID, - kExtension, - kAddExtension, - kUpdate, - kKeepName, - kStartSignature, - kFinishSignature, - kAssociate - } - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - internal delegate int CreateObjectDelegate( - [In] ref Guid classID, - [In] ref Guid interfaceID, - //out IntPtr outObject); - [MarshalAs(UnmanagedType.Interface)] out object outObject); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - internal delegate int GetHandlerPropertyDelegate( - ArchivePropId propID, - ref PropVariant value); // PROPVARIANT - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - internal delegate int GetNumberOfFormatsDelegate(out uint numFormats); - - [UnmanagedFunctionPointer(CallingConvention.StdCall)] - internal delegate int GetHandlerProperty2Delegate( - uint formatIndex, - ArchivePropId propID, - ref PropVariant value); // PROPVARIANT - - internal class StreamWrapper : IDisposable - { - protected Stream BaseStream; - - protected StreamWrapper(Stream baseStream) - { - this.BaseStream = baseStream; - } - - public void Dispose() - { - this.BaseStream.Close(); - } - - public virtual void Seek(long offset, uint seekOrigin, IntPtr newPosition) - { - long Position = this.BaseStream.Seek(offset, (SeekOrigin) seekOrigin); - - if (newPosition != IntPtr.Zero) - { - Marshal.WriteInt64(newPosition, Position); - } - } - } - - internal class InStreamWrapper : StreamWrapper, ISequentialInStream, IInStream - { - public InStreamWrapper(Stream baseStream) : base(baseStream) - { - } - - public uint Read(byte[] data, uint size) - { - return (uint) this.BaseStream.Read(data, 0, (int) size); - } - } - - internal class OutStreamWrapper : StreamWrapper, ISequentialOutStream, IOutStream - { - public OutStreamWrapper(Stream baseStream) : base(baseStream) - { - } - - public int SetSize(long newSize) - { - this.BaseStream.SetLength(newSize); - return 0; - } - - public int Write(byte[] data, uint size, IntPtr processedSize) - { - this.BaseStream.Write(data, 0, (int) size); - - if (processedSize != IntPtr.Zero) - { - Marshal.WriteInt32(processedSize, (int) size); - } - - return 0; - } - } -} \ No newline at end of file diff --git a/pack.bat b/pack.bat index 6d6b061..4e76819 100644 --- a/pack.bat +++ b/pack.bat @@ -1 +1 @@ -vpk pack -u MangaJaNaiConverterGui -v 1.0.0 -p ".\MangaJaNaiConverterGui\bin\Release\net8.0\publish\win-x64" -i ./MangaJaNaiConverterGui/assets/logo.ico -e MangaJaNaiConverterGui.exe --delta none \ No newline at end of file +vpk pack -u MangaJaNaiConverterGui -v 1.0.1 -p ".\MangaJaNaiConverterGui\bin\Release\net8.0\publish\win-x64" -i ./MangaJaNaiConverterGui/assets/logo.ico -e MangaJaNaiConverterGui.exe \ No newline at end of file