From 775c623024b211c407476edabdb035e96d93ae15 Mon Sep 17 00:00:00 2001 From: logchan Date: Wed, 12 Feb 2020 22:09:43 -0500 Subject: [PATCH] initial commit --- .editorconfig | 197 +++++++++++++++++++ .gitignore | 3 + README.md | 14 ++ chuni-hands.sln | 37 ++++ chuni-hands.sln.DotSettings | 2 + chuni-hands/App.config | 6 + chuni-hands/App.xaml | 9 + chuni-hands/App.xaml.cs | 9 + chuni-hands/ChuniCanvas.cs | 36 ++++ chuni-hands/Config.cs | 18 ++ chuni-hands/Helpers.cs | 22 +++ chuni-hands/Logger.cs | 33 ++++ chuni-hands/MainWindow.xaml | 47 +++++ chuni-hands/MainWindow.xaml.cs | 154 +++++++++++++++ chuni-hands/Properties/AssemblyInfo.cs | 53 +++++ chuni-hands/Properties/Resources.Designer.cs | 63 ++++++ chuni-hands/Properties/Resources.resx | 117 +++++++++++ chuni-hands/Properties/Settings.Designer.cs | 26 +++ chuni-hands/Properties/Settings.settings | 7 + chuni-hands/Sensor.cs | 82 ++++++++ chuni-hands/chuni-hands.csproj | 135 +++++++++++++ chuni-hands/packages.config | 4 + 22 files changed, 1074 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 README.md create mode 100644 chuni-hands.sln create mode 100644 chuni-hands.sln.DotSettings create mode 100644 chuni-hands/App.config create mode 100644 chuni-hands/App.xaml create mode 100644 chuni-hands/App.xaml.cs create mode 100644 chuni-hands/ChuniCanvas.cs create mode 100644 chuni-hands/Config.cs create mode 100644 chuni-hands/Helpers.cs create mode 100644 chuni-hands/Logger.cs create mode 100644 chuni-hands/MainWindow.xaml create mode 100644 chuni-hands/MainWindow.xaml.cs create mode 100644 chuni-hands/Properties/AssemblyInfo.cs create mode 100644 chuni-hands/Properties/Resources.Designer.cs create mode 100644 chuni-hands/Properties/Resources.resx create mode 100644 chuni-hands/Properties/Settings.Designer.cs create mode 100644 chuni-hands/Properties/Settings.settings create mode 100644 chuni-hands/Sensor.cs create mode 100644 chuni-hands/chuni-hands.csproj create mode 100644 chuni-hands/packages.config diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..11b8def --- /dev/null +++ b/.editorconfig @@ -0,0 +1,197 @@ +# 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 + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:silent +csharp_style_var_for_built_in_types = false:silent +csharp_style_var_when_type_is_apparent = false:silent + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_switch_expression = true:suggestion + +# Null-checking preferences +csharp_style_conditional_delegate_call = true:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async + +# Code-block preferences +csharp_prefer_braces = true:silent +csharp_prefer_simple_using_statement = true:suggestion + +# Expression-level preferences +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_assignment_preference = discard_variable:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +#### 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 = none +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/.gitignore b/.gitignore new file mode 100644 index 0000000..a9b29e0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vs/ +bin/ +obj/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..db8dab9 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +chuni-hands +--- + +Mysterious tool that detects things. + +## User requirements + +- Camera +- .Net Framework 4.7.1 + +## Build requirements + +- VS2019 +- [emgucv](https://github.com/emgucv/emgucv) diff --git a/chuni-hands.sln b/chuni-hands.sln new file mode 100644 index 0000000..b6c1ef4 --- /dev/null +++ b/chuni-hands.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29728.190 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "chuni-hands", "chuni-hands\chuni-hands.csproj", "{F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Emgu.CV.Runtime.Windows", "C:\Emgu\emgucv-windesktop 4.2.0.3662\Emgu.CV.Runtime\Windows\Emgu.CV.Runtime.Windows.shproj", "{ADC3C8E5-EBCD-4D3C-B3A4-20CFE0E42FC1}" +EndProject +Global + GlobalSection(SharedMSBuildProjectFiles) = preSolution + C:\Emgu\emgucv-windesktop 4.2.0.3662\Emgu.CV.Runtime\Windows\Emgu.CV.Runtime.Windows.projitems*{adc3c8e5-ebcd-4d3c-b3a4-20cfe0e42fc1}*SharedItemsImports = 13 + C:\Emgu\emgucv-windesktop 4.2.0.3662\Emgu.CV.Runtime\Windows\Emgu.CV.Runtime.Windows.projitems*{f1fdd6c6-7b45-403d-9a53-8e9ca600bc3f}*SharedItemsImports = 4 + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x64.ActiveCfg = Debug|x64 + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x64.Build.0 = Debug|x64 + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|Any CPU.Build.0 = Release|Any CPU + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x64.ActiveCfg = Release|x64 + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F6A72ED9-3F5E-4A27-84DB-FB8BCC2AD38F} + EndGlobalSection +EndGlobal diff --git a/chuni-hands.sln.DotSettings b/chuni-hands.sln.DotSettings new file mode 100644 index 0000000..6005c6e --- /dev/null +++ b/chuni-hands.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/chuni-hands/App.config b/chuni-hands/App.config new file mode 100644 index 0000000..ecdcf8a --- /dev/null +++ b/chuni-hands/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/chuni-hands/App.xaml b/chuni-hands/App.xaml new file mode 100644 index 0000000..f67f3c7 --- /dev/null +++ b/chuni-hands/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/chuni-hands/App.xaml.cs b/chuni-hands/App.xaml.cs new file mode 100644 index 0000000..fb61abe --- /dev/null +++ b/chuni-hands/App.xaml.cs @@ -0,0 +1,9 @@ +using System.Windows; + +namespace chuni_hands { + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application { + } +} diff --git a/chuni-hands/ChuniCanvas.cs b/chuni-hands/ChuniCanvas.cs new file mode 100644 index 0000000..1eb4f75 --- /dev/null +++ b/chuni-hands/ChuniCanvas.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace chuni_hands { + internal sealed class ChuniCanvas : Canvas { + + public IEnumerable Sensors { get; set; } + + public ImageSource Image { + get => (ImageSource)GetValue(ImageProperty); + set => SetValue(ImageProperty, value); + } + + public static readonly DependencyProperty ImageProperty = + DependencyProperty.Register("Image", typeof(ImageSource), typeof(ChuniCanvas), new PropertyMetadata(null)); + + protected override void OnRender(DrawingContext dc) { + base.OnRender(dc); + + var image = Image; + if (image == null) { + return; + } + + var factor = ActualWidth / image.Width; + factor = Math.Min(factor, ActualHeight / image.Height); + var paddingX = (ActualWidth - image.Width * factor) / 2; + var paddingY = (ActualHeight - image.Height * factor) / 2; + + dc.DrawImage(image, new Rect(paddingX, paddingY, image.Width * factor, image.Height * factor)); + } + } +} diff --git a/chuni-hands/Config.cs b/chuni-hands/Config.cs new file mode 100644 index 0000000..810c70c --- /dev/null +++ b/chuni-hands/Config.cs @@ -0,0 +1,18 @@ +namespace chuni_hands { + public sealed class Config { + public int OffsetX { get; set; } + public int OffsetY { get; set; } + public int Exposure { get; set; } = -6; + public int SensorSize { get; set; } = 21; + public double Threshold { get; set; } = 10; + public int Distance { get; set; } = 40; + + public int BoostrapSeconds { get; set; } = 2; + public int CaptureWidth { get; set; } = 640; + public int CaptureHeight { get; set; } = 480; + public int Fps { get; set; } = 60; + public bool LogDiff { get; set; } + public string SendKeyMode { get; set; } = "be"; + public string EndPoint { get; set; } = "http://10.233.3.22:4420/update_air"; + } +} diff --git a/chuni-hands/Helpers.cs b/chuni-hands/Helpers.cs new file mode 100644 index 0000000..493a108 --- /dev/null +++ b/chuni-hands/Helpers.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; +using System.IO; +using System.Reflection; +using Newtonsoft.Json; + +namespace chuni_hands { + public static class Helpers { + public static T Deserialize(string path) { + var content = File.ReadAllText(path); + return JsonConvert.DeserializeObject(content); + } + + public static void Serialize(object o, string path) { + var content = JsonConvert.SerializeObject(o, Formatting.Indented); + File.WriteAllText(path, content); + } + + public static string GetVersion() { + return FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; + } + } +} diff --git a/chuni-hands/Logger.cs b/chuni-hands/Logger.cs new file mode 100644 index 0000000..09e1727 --- /dev/null +++ b/chuni-hands/Logger.cs @@ -0,0 +1,33 @@ +using System; + +namespace chuni_hands { + public static class Logger { + public delegate void LogHandler(string log); + + public static event LogHandler LogAdded; + + public static void Log(LogLevel level, string msg) { + var time = DateTime.Now.ToString("HH:mm:ss.ff"); + var log = $"{time} [{level}] {msg}"; + LogAdded?.Invoke(log); + } + + public static void Info(string msg) { + Log(LogLevel.Info, msg); + } + + public static void Warning(string msg) { + Log(LogLevel.Warning, msg); + } + + public static void Error(string msg) { + Log(LogLevel.Error, msg); + } + + public enum LogLevel { + Info, + Warning, + Error + } + } +} diff --git a/chuni-hands/MainWindow.xaml b/chuni-hands/MainWindow.xaml new file mode 100644 index 0000000..72452fe --- /dev/null +++ b/chuni-hands/MainWindow.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + Threshold + + + + + Distance + + + + X + + + + Y + + + Log diff + + + + + + + + diff --git a/chuni-hands/MainWindow.xaml.cs b/chuni-hands/MainWindow.xaml.cs new file mode 100644 index 0000000..04977cf --- /dev/null +++ b/chuni-hands/MainWindow.xaml.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Emgu.CV; + +namespace chuni_hands { + public partial class MainWindow : Window { + private const string ConfigFile = "chuni-hands.json"; + + private VideoCapture _capture; + private Config _config = new Config(); + private volatile bool _closing = false; + private readonly Mat _mat = new Mat(); + private byte[] _matData = new byte[0]; + private readonly List _sensors = new List(5); + private Task _captureTask; + private bool _hasPendingReset = false; + private HttpClient _http = new HttpClient(); + + public Config Config => _config; + + public MainWindow() { + if (File.Exists(ConfigFile)) { + _config = Helpers.Deserialize(ConfigFile); + } + + InitializeComponent(); + Title += " version " + Helpers.GetVersion(); + + TheCanvas.Sensors = _sensors; + + Logger.LogAdded += log => { + LogBox.AppendText(log); + LogBox.AppendText(Environment.NewLine); + LogBox.ScrollToEnd(); + }; + } + + private void FrameUpdate() { + + // compute + + foreach (var sensor in _sensors) { + sensor.Update(_mat, _hasPendingReset); + } + _hasPendingReset = false; + + foreach (var sensor in _sensors) { + sensor.Draw(_mat); + } + + // send key + + SendKey(); + + // update display + + var length = _mat.Rows * _mat.Cols * _mat.NumberOfChannels; + if (_matData.Length < length) { + _matData = new byte[length]; + } + + _mat.CopyTo(_matData); + + var bm = BitmapSource.Create(_mat.Cols, _mat.Rows, 96, 96, PixelFormats.Bgr24, null, _matData, _mat.Cols * _mat.NumberOfChannels); + TheCanvas.Image = bm; + TheCanvas.InvalidateVisual(); + } + + private void SendKey() { + if (IsActive) { + return; + } + + if (_sensors.All(s => !s.StateChanged)) { + return; + } + + switch (_config.SendKeyMode) { + case "be": { + var airKeys = String.Concat(from sensor in _sensors select sensor.Active ? "1" : "0"); + _http.GetAsync(_config.EndPoint + "?k=" + airKeys); + } + break; + default: + throw new Exception("unknown SendKeyMode"); + } + } + + private void Window_Loaded(object sender, RoutedEventArgs e) { + var cap = new VideoCapture(); + cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, _config.CaptureWidth); + cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, _config.CaptureHeight); + cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Autofocus, 0); + cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Exposure, _config.Exposure); + + _capture = cap; + _capture.Read(_mat); + _config.CaptureWidth = _mat.Cols; + _config.CaptureHeight = _mat.Rows; + + for (var i = 0; i < 6; ++i) { + _sensors.Add(new Sensor(i, _config)); + } + + _captureTask = Task.Run(CaptureLoop); + } + + private void CaptureLoop() { + // give camera some time to auto adjust, so user don't need to press reset right after start + var bootstrapFrames = _config.BoostrapSeconds * _config.Fps; + + while (!_closing) { + _capture.Read(_mat); + if (bootstrapFrames > 0) { + --bootstrapFrames; + } + else { + Dispatcher?.BeginInvoke(new Action(FrameUpdate)); + } + + // what the...?? well, it works + Thread.Sleep(1000 / _config.Fps); + } + } + + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { + _closing = true; + _captureTask.Wait(); + + _capture.Stop(); + _capture.Dispose(); + + Helpers.Serialize(_config, ConfigFile); + } + + private void ResetButton_Click(object sender, RoutedEventArgs e) { + _hasPendingReset = true; + } + + private void SetThresholdButton_Click(object sender, RoutedEventArgs e) { + if (Double.TryParse(ThresholdBox.Text, out var v)) { + _config.Threshold = v; + } + } + } +} diff --git a/chuni-hands/Properties/AssemblyInfo.cs b/chuni-hands/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8b5010d --- /dev/null +++ b/chuni-hands/Properties/AssemblyInfo.cs @@ -0,0 +1,53 @@ +using System.Reflection; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("chuni-hands")] +[assembly: AssemblyDescription("chuni sensor simulator")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("chuni-hands")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.1.0.0")] +[assembly: AssemblyFileVersion("0.1.0.0")] diff --git a/chuni-hands/Properties/Resources.Designer.cs b/chuni-hands/Properties/Resources.Designer.cs new file mode 100644 index 0000000..355e858 --- /dev/null +++ b/chuni-hands/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace chuni_hands.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("chuni_hands.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/chuni-hands/Properties/Resources.resx b/chuni-hands/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/chuni-hands/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/chuni-hands/Properties/Settings.Designer.cs b/chuni-hands/Properties/Settings.Designer.cs new file mode 100644 index 0000000..fcef9b8 --- /dev/null +++ b/chuni-hands/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace chuni_hands.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/chuni-hands/Properties/Settings.settings b/chuni-hands/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/chuni-hands/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/chuni-hands/Sensor.cs b/chuni-hands/Sensor.cs new file mode 100644 index 0000000..6b96d41 --- /dev/null +++ b/chuni-hands/Sensor.cs @@ -0,0 +1,82 @@ +using System; +using Emgu.CV; +using Emgu.CV.Structure; + +namespace chuni_hands { + internal sealed class Sensor { + public int X { get; private set; } + public int Y { get; private set; } + public bool Active { get; set; } + public bool StateChanged { get; set; } + + private Mat _startValue; + private Config _config; + private int _id; + + public Sensor(int id, Config config) { + _id = id; + _config = config; + } + + private Mat GetPartial(Mat frame) { + var sz = _config.SensorSize; + return new Mat(frame, new Range(Y - sz / 2, Y + sz / 2 + 1), new Range(X - sz / 2, X + sz / 2 + 1)); + } + + public void Update(Mat frame, bool forceInit) { + + // reposition + + X = _config.CaptureWidth / 2 + _config.OffsetX; + X = Math.Max(Math.Min(X, _config.CaptureWidth - _config.SensorSize), _config.SensorSize); + + Y = _config.CaptureHeight / 2 + _config.OffsetY + (_id - 3) * _config.Distance; + Y = Math.Max(Math.Min(Y, _config.CaptureHeight - _config.SensorSize), _config.SensorSize); + + // check area + + var pixels = GetPartial(frame); + + if (_startValue == null || forceInit) { + _startValue = new Mat(pixels.Size, Emgu.CV.CvEnum.DepthType.Cv64F, pixels.NumberOfChannels); + pixels.ConvertTo(_startValue, _startValue.Depth); + _startValue /= 255; + + Active = false; + StateChanged = false; + return; + } + + var pixelsD = new Mat(pixels.Size, Emgu.CV.CvEnum.DepthType.Cv64F, pixels.NumberOfChannels); + pixels.ConvertTo(pixelsD, pixelsD.Depth); + pixelsD /= 255; + + var matDiff = pixelsD - _startValue; + var diff = matDiff.Dot(matDiff); + if (_id == 0 && _config.LogDiff) { + Logger.Info($"diff: {diff}"); + } + + var active = diff > _config.Threshold; + StateChanged = active != Active; + Active = active; + } + + public void Draw(Mat frame) { + var pixels = GetPartial(frame); + var color = new byte[] { 0, 0, 0 }; + if (Active) { + color[0] = 255; + } + else { + color[2] = 255; + } + + for (var y = 0; y < pixels.Rows; ++y) { + for (var x = 0; x < pixels.Cols; ++x) { + pixels.Row(y).Col(x).SetTo(color); + } + } + } + } +} diff --git a/chuni-hands/chuni-hands.csproj b/chuni-hands/chuni-hands.csproj new file mode 100644 index 0000000..ca644f3 --- /dev/null +++ b/chuni-hands/chuni-hands.csproj @@ -0,0 +1,135 @@ + + + + + Debug + AnyCPU + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F} + WinExe + chuni_hands + chuni-hands + v4.7.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + + C:\Emgu\emgucv-windesktop 4.2.0.3662\bin\Emgu.CV.World.Netstandard.dll + + + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + + + + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + \ No newline at end of file diff --git a/chuni-hands/packages.config b/chuni-hands/packages.config new file mode 100644 index 0000000..a9de8b5 --- /dev/null +++ b/chuni-hands/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file