├── .editorconfig ├── .gitignore ├── CameraHelper ├── AssemblyInfo.cpp ├── CameraHelper.cpp ├── CameraHelper.h ├── CameraHelper.vcxproj ├── CameraHelper.vcxproj.filters ├── CameraHelper.vcxproj.user ├── Resource.h ├── app.ico ├── app.rc ├── pch.cpp └── pch.h ├── CameraHelperTest ├── App.config ├── CameraHelperTest.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs ├── README.md ├── chuni-hands.sln ├── chuni-hands.sln.DotSettings └── chuni-hands ├── App.config ├── App.xaml ├── App.xaml.cs ├── ChuniCanvas.cs ├── ChuniIO.cs ├── Config.cs ├── Helpers.cs ├── Logger.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── Sensor.cs ├── Styles.xaml ├── chuni-hands.csproj └── packages.config /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | 7 | #### Core EditorConfig Options #### 8 | 9 | # Indentation and spacing 10 | indent_size = 4 11 | indent_style = space 12 | tab_width = 4 13 | 14 | # New line preferences 15 | end_of_line = crlf 16 | insert_final_newline = false 17 | 18 | #### .NET Coding Conventions #### 19 | 20 | # Organize usings 21 | dotnet_separate_import_directive_groups = false 22 | dotnet_sort_system_directives_first = false 23 | 24 | # this. and Me. preferences 25 | dotnet_style_qualification_for_event = false:silent 26 | dotnet_style_qualification_for_field = false:silent 27 | dotnet_style_qualification_for_method = false:silent 28 | dotnet_style_qualification_for_property = false:silent 29 | 30 | # Language keywords vs BCL types preferences 31 | dotnet_style_predefined_type_for_locals_parameters_members = true:silent 32 | dotnet_style_predefined_type_for_member_access = true:silent 33 | 34 | # Parentheses preferences 35 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent 36 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent 37 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent 38 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent 39 | 40 | # Modifier preferences 41 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent 42 | 43 | # Expression-level preferences 44 | dotnet_style_coalesce_expression = true:suggestion 45 | dotnet_style_collection_initializer = true:suggestion 46 | dotnet_style_explicit_tuple_names = true:suggestion 47 | dotnet_style_null_propagation = true:suggestion 48 | dotnet_style_object_initializer = true:suggestion 49 | dotnet_style_prefer_auto_properties = true:none 50 | dotnet_style_prefer_compound_assignment = true:suggestion 51 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent 52 | dotnet_style_prefer_conditional_expression_over_return = true:silent 53 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion 54 | dotnet_style_prefer_inferred_tuple_names = true:suggestion 55 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion 56 | 57 | # Field preferences 58 | dotnet_style_readonly_field = true:suggestion 59 | 60 | # Parameter preferences 61 | dotnet_code_quality_unused_parameters = all:suggestion 62 | 63 | #### C# Coding Conventions #### 64 | 65 | # var preferences 66 | csharp_style_var_elsewhere = false:silent 67 | csharp_style_var_for_built_in_types = false:silent 68 | csharp_style_var_when_type_is_apparent = false:silent 69 | 70 | # Expression-bodied members 71 | csharp_style_expression_bodied_accessors = true:silent 72 | csharp_style_expression_bodied_constructors = false:silent 73 | csharp_style_expression_bodied_indexers = true:silent 74 | csharp_style_expression_bodied_lambdas = true:silent 75 | csharp_style_expression_bodied_local_functions = false:silent 76 | csharp_style_expression_bodied_methods = false:silent 77 | csharp_style_expression_bodied_operators = false:silent 78 | csharp_style_expression_bodied_properties = true:silent 79 | 80 | # Pattern matching preferences 81 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 82 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 83 | csharp_style_prefer_switch_expression = true:suggestion 84 | 85 | # Null-checking preferences 86 | csharp_style_conditional_delegate_call = true:suggestion 87 | 88 | # Modifier preferences 89 | csharp_prefer_static_local_function = true:suggestion 90 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async 91 | 92 | # Code-block preferences 93 | csharp_prefer_braces = true:silent 94 | csharp_prefer_simple_using_statement = true:suggestion 95 | 96 | # Expression-level preferences 97 | csharp_prefer_simple_default_expression = true:suggestion 98 | csharp_style_deconstructed_variable_declaration = true:suggestion 99 | csharp_style_inlined_variable_declaration = true:suggestion 100 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 101 | csharp_style_prefer_index_operator = true:suggestion 102 | csharp_style_prefer_range_operator = true:suggestion 103 | csharp_style_throw_expression = true:suggestion 104 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 105 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 106 | 107 | # 'using' directive preferences 108 | csharp_using_directive_placement = outside_namespace:silent 109 | 110 | #### C# Formatting Rules #### 111 | 112 | # New line preferences 113 | csharp_new_line_before_catch = true 114 | csharp_new_line_before_else = true 115 | csharp_new_line_before_finally = true 116 | csharp_new_line_before_members_in_anonymous_types = true 117 | csharp_new_line_before_members_in_object_initializers = true 118 | csharp_new_line_before_open_brace = none 119 | csharp_new_line_between_query_expression_clauses = true 120 | 121 | # Indentation preferences 122 | csharp_indent_block_contents = true 123 | csharp_indent_braces = false 124 | csharp_indent_case_contents = true 125 | csharp_indent_case_contents_when_block = true 126 | csharp_indent_labels = one_less_than_current 127 | csharp_indent_switch_labels = true 128 | 129 | # Space preferences 130 | csharp_space_after_cast = false 131 | csharp_space_after_colon_in_inheritance_clause = true 132 | csharp_space_after_comma = true 133 | csharp_space_after_dot = false 134 | csharp_space_after_keywords_in_control_flow_statements = true 135 | csharp_space_after_semicolon_in_for_statement = true 136 | csharp_space_around_binary_operators = before_and_after 137 | csharp_space_around_declaration_statements = false 138 | csharp_space_before_colon_in_inheritance_clause = true 139 | csharp_space_before_comma = false 140 | csharp_space_before_dot = false 141 | csharp_space_before_open_square_brackets = false 142 | csharp_space_before_semicolon_in_for_statement = false 143 | csharp_space_between_empty_square_brackets = false 144 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 145 | csharp_space_between_method_call_name_and_opening_parenthesis = false 146 | csharp_space_between_method_call_parameter_list_parentheses = false 147 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 148 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 149 | csharp_space_between_method_declaration_parameter_list_parentheses = false 150 | csharp_space_between_parentheses = false 151 | csharp_space_between_square_brackets = false 152 | 153 | # Wrapping preferences 154 | csharp_preserve_single_line_blocks = true 155 | csharp_preserve_single_line_statements = true 156 | 157 | #### Naming styles #### 158 | 159 | # Naming rules 160 | 161 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion 162 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface 163 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i 164 | 165 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion 166 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types 167 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case 168 | 169 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion 170 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members 171 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case 172 | 173 | # Symbol specifications 174 | 175 | dotnet_naming_symbols.interface.applicable_kinds = interface 176 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 177 | dotnet_naming_symbols.interface.required_modifiers = 178 | 179 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum 180 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 181 | dotnet_naming_symbols.types.required_modifiers = 182 | 183 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method 184 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected 185 | dotnet_naming_symbols.non_field_members.required_modifiers = 186 | 187 | # Naming styles 188 | 189 | dotnet_naming_style.pascal_case.required_prefix = 190 | dotnet_naming_style.pascal_case.required_suffix = 191 | dotnet_naming_style.pascal_case.word_separator = 192 | dotnet_naming_style.pascal_case.capitalization = pascal_case 193 | 194 | dotnet_naming_style.begins_with_i.required_prefix = I 195 | dotnet_naming_style.begins_with_i.required_suffix = 196 | dotnet_naming_style.begins_with_i.word_separator = 197 | dotnet_naming_style.begins_with_i.capitalization = pascal_case 198 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | bin/ 3 | obj/ 4 | packages/ 5 | *.user 6 | -------------------------------------------------------------------------------- /CameraHelper/AssemblyInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | using namespace System; 4 | using namespace System::Reflection; 5 | using namespace System::Runtime::CompilerServices; 6 | using namespace System::Runtime::InteropServices; 7 | using namespace System::Security::Permissions; 8 | 9 | [assembly:AssemblyTitleAttribute(L"CameraHelper")]; 10 | [assembly:AssemblyDescriptionAttribute(L"")]; 11 | [assembly:AssemblyConfigurationAttribute(L"")]; 12 | [assembly:AssemblyCompanyAttribute(L"")]; 13 | [assembly:AssemblyProductAttribute(L"CameraHelper")]; 14 | [assembly:AssemblyCopyrightAttribute(L"Copyright (c) 2020")]; 15 | [assembly:AssemblyTrademarkAttribute(L"")]; 16 | [assembly:AssemblyCultureAttribute(L"")]; 17 | 18 | [assembly:AssemblyVersionAttribute("1.0.*")]; 19 | 20 | [assembly:ComVisible(false)]; 21 | 22 | [assembly:CLSCompliantAttribute(true)]; 23 | -------------------------------------------------------------------------------- /CameraHelper/CameraHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "pch.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "CameraHelper.h" 8 | 9 | 10 | using namespace System; 11 | using namespace System::Collections::Generic; 12 | using namespace CameraHelper; 13 | 14 | List^ ::CameraHelper::CameraHelper::GetCameras() { 15 | auto list = gcnew List; 16 | 17 | HRESULT hr; 18 | ICreateDevEnum* pSysDevEnum; 19 | hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, 20 | IID_ICreateDevEnum, (void**)&pSysDevEnum); 21 | if (FAILED(hr)) { 22 | return list; 23 | } 24 | 25 | IEnumMoniker *pEnumCat; 26 | hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0); 27 | if (hr != S_OK) { 28 | pSysDevEnum->Release(); 29 | return list; 30 | } 31 | 32 | IMoniker *pMoniker; 33 | ULONG cFetched; 34 | auto id = 0; 35 | while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { 36 | IPropertyBag* pPropBag; 37 | hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag); 38 | if (SUCCEEDED(hr)) { 39 | VARIANT varName; 40 | VariantInit(&varName); 41 | hr = pPropBag->Read(L"FriendlyName", &varName, 0); 42 | if (SUCCEEDED(hr)) { 43 | auto info = gcnew CameraInfo; 44 | info->Id = id++; 45 | info->Name = gcnew String(varName.bstrVal); 46 | list->Add(info); 47 | } 48 | VariantClear(&varName); 49 | pPropBag->Release(); 50 | } 51 | pMoniker->Release(); 52 | } 53 | pEnumCat->Release(); 54 | 55 | return list; 56 | } -------------------------------------------------------------------------------- /CameraHelper/CameraHelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | using namespace System; 4 | 5 | namespace CameraHelper { 6 | public ref class CameraInfo { 7 | public: 8 | property int Id; 9 | property String^ Name; 10 | }; 11 | 12 | public ref class CameraHelper { 13 | public: 14 | static System::Collections::Generic::List^ GetCameras(); 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /CameraHelper/CameraHelper.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F} 24 | v4.5 25 | ManagedCProj 26 | CameraHelper 27 | 10.0 28 | 29 | 30 | 31 | DynamicLibrary 32 | true 33 | v142 34 | true 35 | Unicode 36 | 37 | 38 | DynamicLibrary 39 | false 40 | v142 41 | true 42 | Unicode 43 | 44 | 45 | DynamicLibrary 46 | true 47 | v142 48 | true 49 | Unicode 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v142 55 | true 56 | Unicode 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | bin\$(Platform)\$(Configuration)\ 78 | obj\$(Platform)\$(Configuration)\ 79 | 80 | 81 | bin\$(Platform)\$(Configuration)\ 82 | obj\$(Platform)\$(Configuration)\ 83 | 84 | 85 | 86 | Use 87 | pch.h 88 | Level3 89 | NDEBUG;%(PreprocessorDefinitions) 90 | 91 | 92 | Strmiids.lib;ole32.lib;OleAut32.lib 93 | 94 | 95 | 96 | 97 | Use 98 | pch.h 99 | Level3 100 | WIN32;_DEBUG;%(PreprocessorDefinitions) 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | Use 109 | pch.h 110 | Level3 111 | _DEBUG;%(PreprocessorDefinitions) 112 | 113 | 114 | Strmiids.lib;ole32.lib;OleAut32.lib 115 | 116 | 117 | 118 | 119 | Use 120 | pch.h 121 | Level3 122 | WIN32;NDEBUG;%(PreprocessorDefinitions) 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | Create 138 | Create 139 | Create 140 | Create 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /CameraHelper/CameraHelper.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | 40 | 41 | Resource Files 42 | 43 | 44 | 45 | 46 | Resource Files 47 | 48 | 49 | -------------------------------------------------------------------------------- /CameraHelper/CameraHelper.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /CameraHelper/Resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by app.rc 4 | -------------------------------------------------------------------------------- /CameraHelper/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logchan/chuni-hands/1af7332ba238e180d0d814952cf428ca8b9e173d/CameraHelper/app.ico -------------------------------------------------------------------------------- /CameraHelper/app.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/logchan/chuni-hands/1af7332ba238e180d0d814952cf428ca8b9e173d/CameraHelper/app.rc -------------------------------------------------------------------------------- /CameraHelper/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /CameraHelper/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | 12 | #endif //PCH_H 13 | -------------------------------------------------------------------------------- /CameraHelperTest/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CameraHelperTest/CameraHelperTest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {13F60778-2DF6-4723-BB87-2A433B822307} 8 | Exe 9 | CameraHelperTest 10 | CameraHelperTest 11 | v4.5 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | 7.3 41 | prompt 42 | MinimumRecommendedRules.ruleset 43 | true 44 | 45 | 46 | bin\x64\Release\ 47 | TRACE 48 | true 49 | pdbonly 50 | x64 51 | 7.3 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | true 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | {720d3af0-6a71-422b-8a93-1deece9e330f} 76 | CameraHelper 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /CameraHelperTest/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace CameraHelperTest { 8 | class Program { 9 | static void Main(string[] args) { 10 | var cameras = CameraHelper.CameraHelper.GetCameras(); 11 | foreach (var camera in cameras) { 12 | Console.WriteLine($"{camera.Id} {camera.Name}"); 13 | } 14 | 15 | Console.ReadLine(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CameraHelperTest/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("CameraHelperTest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("CameraHelperTest")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("13f60778-2df6-4723-bb87-2a433b822307")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | chuni-hands 2 | --- 3 | 4 | Mysterious tool that detects things. 5 | 6 | ## User requirements 7 | 8 | - Camera 9 | - .Net Framework 4.7.1 10 | 11 | ## Build requirements 12 | 13 | - VS2019 14 | - [emgucv](https://github.com/emgucv/emgucv) 15 | -------------------------------------------------------------------------------- /chuni-hands.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29728.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "chuni-hands", "chuni-hands\chuni-hands.csproj", "{F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}" 7 | EndProject 8 | 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}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CameraHelper", "CameraHelper\CameraHelper.vcxproj", "{720D3AF0-6A71-422B-8A93-1DEECE9E330F}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CameraHelperTest", "CameraHelperTest\CameraHelperTest.csproj", "{13F60778-2DF6-4723-BB87-2A433B822307}" 13 | EndProject 14 | Global 15 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 16 | C:\Emgu\emgucv-windesktop 4.2.0.3662\Emgu.CV.Runtime\Windows\Emgu.CV.Runtime.Windows.projitems*{adc3c8e5-ebcd-4d3c-b3a4-20cfe0e42fc1}*SharedItemsImports = 13 17 | C:\Emgu\emgucv-windesktop 4.2.0.3662\Emgu.CV.Runtime\Windows\Emgu.CV.Runtime.Windows.projitems*{f1fdd6c6-7b45-403d-9a53-8e9ca600bc3f}*SharedItemsImports = 4 18 | EndGlobalSection 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Debug|x64 = Debug|x64 22 | Debug|x86 = Debug|x86 23 | Release|Any CPU = Release|Any CPU 24 | Release|x64 = Release|x64 25 | Release|x86 = Release|x86 26 | EndGlobalSection 27 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 28 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x64.ActiveCfg = Debug|x64 31 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x64.Build.0 = Debug|x64 32 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x86.ActiveCfg = Debug|x86 33 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x86.Build.0 = Debug|x86 34 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x64.ActiveCfg = Release|x64 37 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x64.Build.0 = Release|x64 38 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x86.ActiveCfg = Release|x86 39 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x86.Build.0 = Release|x86 40 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|Any CPU.ActiveCfg = Debug|Win32 41 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x64.ActiveCfg = Debug|x64 42 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x64.Build.0 = Debug|x64 43 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x86.ActiveCfg = Debug|Win32 44 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x86.Build.0 = Debug|Win32 45 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|Any CPU.ActiveCfg = Release|Win32 46 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x64.ActiveCfg = Release|x64 47 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x64.Build.0 = Release|x64 48 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x86.ActiveCfg = Release|Win32 49 | {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x86.Build.0 = Release|Win32 50 | {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x64.ActiveCfg = Debug|Any CPU 53 | {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x64.Build.0 = Debug|Any CPU 54 | {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x86.ActiveCfg = Debug|Any CPU 55 | {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x86.Build.0 = Debug|Any CPU 56 | {13F60778-2DF6-4723-BB87-2A433B822307}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {13F60778-2DF6-4723-BB87-2A433B822307}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x64.ActiveCfg = Release|x64 59 | {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x64.Build.0 = Release|x64 60 | {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x86.ActiveCfg = Release|Any CPU 61 | {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x86.Build.0 = Release|Any CPU 62 | EndGlobalSection 63 | GlobalSection(SolutionProperties) = preSolution 64 | HideSolutionNode = FALSE 65 | EndGlobalSection 66 | GlobalSection(ExtensibilityGlobals) = postSolution 67 | SolutionGuid = {F6A72ED9-3F5E-4A27-84DB-FB8BCC2AD38F} 68 | EndGlobalSection 69 | EndGlobal 70 | -------------------------------------------------------------------------------- /chuni-hands.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True -------------------------------------------------------------------------------- /chuni-hands/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /chuni-hands/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /chuni-hands/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace chuni_hands { 4 | /// 5 | /// Interaction logic for App.xaml 6 | /// 7 | public partial class App : Application { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chuni-hands/ChuniCanvas.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Media; 6 | 7 | namespace chuni_hands { 8 | internal sealed class ChuniCanvas : Canvas { 9 | 10 | private const double SensorThickness = 4.0; 11 | 12 | public IEnumerable Sensors { get; set; } 13 | 14 | public ImageSource Image { 15 | get => (ImageSource)GetValue(ImageProperty); 16 | set => SetValue(ImageProperty, value); 17 | } 18 | 19 | public static readonly DependencyProperty ImageProperty = 20 | DependencyProperty.Register("Image", typeof(ImageSource), typeof(ChuniCanvas), new PropertyMetadata(null)); 21 | 22 | public bool DrawImage { 23 | get { return (bool)GetValue(DrawImageProperty); } 24 | set { SetValue(DrawImageProperty, value); } 25 | } 26 | 27 | public static readonly DependencyProperty DrawImageProperty = 28 | DependencyProperty.Register("DrawImage", typeof(bool), typeof(ChuniCanvas), new PropertyMetadata(false)); 29 | 30 | protected override void OnRender(DrawingContext dc) { 31 | base.OnRender(dc); 32 | 33 | dc.DrawRectangle(Brushes.LightGray, null, new Rect(0, 0, ActualWidth, ActualHeight)); 34 | var image = Image; 35 | if (image == null) { 36 | return; 37 | } 38 | 39 | var factor = ActualWidth / image.Width; 40 | factor = Math.Min(factor, ActualHeight / image.Height); 41 | var paddingX = (ActualWidth - image.Width * factor) / 2; 42 | var paddingY = (ActualHeight - image.Height * factor) / 2; 43 | var imageRect = new Rect(paddingX, paddingY, image.Width * factor, image.Height * factor); 44 | 45 | if (DrawImage) { 46 | dc.DrawImage(image, imageRect); 47 | } 48 | 49 | DrawSensors(dc, factor, paddingX, paddingY); 50 | } 51 | 52 | private void DrawSensors(DrawingContext dc, double factor, double paddingX, double paddingY) { 53 | if (Sensors == null) { 54 | return; 55 | } 56 | 57 | foreach (var sensor in Sensors) { 58 | var brush = sensor.Active ? Brushes.Green : Brushes.Red; 59 | var sz = sensor.Size; 60 | var x = paddingX + (sensor.X - sz / 2) * factor; 61 | var y = paddingY + (sensor.Y - sz / 2) * factor; 62 | 63 | dc.DrawRectangle(null, new Pen(brush, SensorThickness), 64 | new Rect(x - SensorThickness / 2, y - SensorThickness / 2, sz * factor + SensorThickness, sz * factor + SensorThickness)); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /chuni-hands/ChuniIO.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.IO.MemoryMappedFiles; 4 | using System.Security.Principal; 5 | 6 | namespace chuni_hands { 7 | internal static class ChuniIO { 8 | public static MemoryMappedFile _buffer; 9 | public static MemoryMappedViewAccessor _accessor; 10 | 11 | private static bool _init = false; 12 | private static readonly int[] SensorMap = new int[] { 1, 0, 3, 2, 5, 4 }; 13 | public static void Send(IList sensors) { 14 | if (!_init) { 15 | Initialize(); 16 | } 17 | 18 | var data = new byte[6]; 19 | for (var i = 0; i < 6; ++i) { 20 | data[SensorMap[i]] = (byte) (sensors[i].Active ? 0x80 : 0x00); 21 | } 22 | 23 | _accessor.WriteArray(0, data, 0, 6); 24 | } 25 | 26 | private static void Initialize() { 27 | var security = new MemoryMappedFileSecurity(); 28 | var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); 29 | var account = sid.Translate(typeof(NTAccount)) as NTAccount; 30 | 31 | Debug.Assert(account != null); 32 | security.AddAccessRule(new System.Security.AccessControl.AccessRule(account.ToString(), MemoryMappedFileRights.FullControl, System.Security.AccessControl.AccessControlType.Allow)); 33 | _buffer = MemoryMappedFile.CreateOrOpen("Local\\BROKENITHM_SHARED_BUFFER", 1024, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, security, System.IO.HandleInheritability.Inheritable); 34 | _accessor = _buffer.CreateViewAccessor(); 35 | 36 | _init = true; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chuni-hands/Config.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace chuni_hands { 4 | public sealed class Config : Helpers.PropertyChangedInvoker { 5 | static Config() { 6 | Helpers.PatchNotifyPropertyChanged(); 7 | } 8 | 9 | public int OffsetX { get; set; } 10 | public int OffsetY { get; set; } 11 | public int Exposure { get; set; } = -6; 12 | public int SensorSize { get; set; } = 21; 13 | public double Threshold { get; set; } = 10; 14 | public int Distance { get; set; } = 40; 15 | 16 | public int CameraId { get; set; } = 0; 17 | public int BootstrapSeconds { get; set; } = 2; 18 | public int CaptureWidth { get; set; } = 640; 19 | public int CaptureHeight { get; set; } = 480; 20 | public int Fps { get; set; } = 60; 21 | public bool LogDiff { get; set; } 22 | public string SendKeyMode { get; set; } = "chuni_io"; 23 | public string EndPoint { get; set; } = "http://10.233.3.22:4420/update_air"; 24 | public bool ShowVideo { get; set; } = true; 25 | 26 | [JsonIgnore] 27 | public bool FreezeVideo { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chuni-hands/Helpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Reflection; 7 | using System.Windows.Threading; 8 | using Harmony; 9 | using Newtonsoft.Json; 10 | 11 | namespace chuni_hands { 12 | public static class Helpers { 13 | public static T Deserialize(string path) { 14 | var content = File.ReadAllText(path); 15 | return JsonConvert.DeserializeObject(content); 16 | } 17 | 18 | public static void Serialize(object o, string path) { 19 | var content = JsonConvert.SerializeObject(o, Formatting.Indented); 20 | File.WriteAllText(path, content); 21 | } 22 | 23 | public static string GetVersion() { 24 | return FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; 25 | } 26 | 27 | private static HarmonyInstance _harmony = HarmonyInstance.Create("co.logu.chuni-hands.helpers"); 28 | 29 | public static void PatchNotifyPropertyChanged() where T : PropertyChangedInvoker { 30 | var type = typeof(T); 31 | var patch = typeof(Helpers).GetMethod("PropertyChangedPostfix", BindingFlags.NonPublic | BindingFlags.Static); 32 | Debug.Assert(patch != null, nameof(patch) + " != null"); 33 | patch = patch.MakeGenericMethod(type); 34 | 35 | foreach (var method in type.GetProperties().Select(p => p.SetMethod)) { 36 | _harmony.Patch(method, new HarmonyMethod(patch)); 37 | } 38 | } 39 | 40 | // ReSharper disable InconsistentNaming 41 | // ReSharper disable once UnusedMember.Local 42 | private static void PropertyChangedPostfix(MethodBase __originalMethod, T __instance) where T : PropertyChangedInvoker { 43 | __instance.InvokePropertyChanged(__originalMethod.Name.Substring(4)); // remove set_ 44 | } 45 | // ReSharper restore InconsistentNaming 46 | 47 | public abstract class PropertyChangedInvoker : INotifyPropertyChanged { 48 | public event PropertyChangedEventHandler PropertyChanged; 49 | public virtual void InvokePropertyChanged(string propertyName) { 50 | Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { 51 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 52 | })); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /chuni-hands/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace chuni_hands { 4 | public static class Logger { 5 | public delegate void LogHandler(string log); 6 | 7 | public static event LogHandler LogAdded; 8 | 9 | public static void Log(LogLevel level, string msg) { 10 | var time = DateTime.Now.ToString("HH:mm:ss.ff"); 11 | var log = $"{time} [{level}] {msg}"; 12 | LogAdded?.Invoke(log); 13 | } 14 | 15 | public static void Info(string msg) { 16 | Log(LogLevel.Info, msg); 17 | } 18 | 19 | public static void Warning(string msg) { 20 | Log(LogLevel.Warning, msg); 21 | } 22 | 23 | public static void Error(string msg) { 24 | Log(LogLevel.Error, msg); 25 | } 26 | 27 | public enum LogLevel { 28 | Info, 29 | Warning, 30 | Error 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chuni-hands/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Video 30 | 31 | Threshold 32 | 33 | 34 | 35 | 36 | Distance 37 | 38 | 39 | 40 | X 41 | 42 | 43 | 44 | Y 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Freeze 56 | Log diff 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /chuni-hands/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net.Http; 5 | using System.Linq; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using System.Windows; 9 | using System.Windows.Media; 10 | using System.Windows.Media.Imaging; 11 | using Emgu.CV; 12 | 13 | namespace chuni_hands { 14 | public partial class MainWindow : Window { 15 | private const string ConfigFile = "chuni-hands.json"; 16 | 17 | private VideoCapture _capture; 18 | private readonly List _sensors = new List(6); 19 | private Mat _mat = new Mat(); 20 | private byte[] _matData = new byte[0]; 21 | private bool _hasPendingReset = false; 22 | 23 | private Task _captureTask; 24 | private volatile bool _closing = false; 25 | 26 | private readonly Config _config = new Config(); 27 | private readonly HttpClient _http = new HttpClient(); 28 | 29 | public Config Config => _config; 30 | 31 | public MainWindow() { 32 | if (File.Exists(ConfigFile)) { 33 | _config = Helpers.Deserialize(ConfigFile); 34 | } 35 | 36 | for (var i = 0; i < 6; ++i) { 37 | _sensors.Add(new Sensor(i, _config)); 38 | } 39 | 40 | InitializeComponent(); 41 | Title += " version " + Helpers.GetVersion(); 42 | 43 | TheCanvas.Sensors = _sensors; 44 | RefreshCameras(); 45 | 46 | Logger.LogAdded += log => { 47 | LogBox.AppendText(log); 48 | LogBox.AppendText(Environment.NewLine); 49 | LogBox.ScrollToEnd(); 50 | }; 51 | } 52 | 53 | private void FrameUpdate() { 54 | 55 | // compute 56 | 57 | foreach (var sensor in _sensors) { 58 | sensor.Update(_mat, _hasPendingReset); 59 | } 60 | _hasPendingReset = false; 61 | 62 | // send key 63 | 64 | SendKey(); 65 | 66 | // update display 67 | 68 | var length = _mat.Rows * _mat.Cols * _mat.NumberOfChannels; 69 | if (_matData.Length < length) { 70 | _matData = new byte[length]; 71 | } 72 | 73 | _mat.CopyTo(_matData); 74 | 75 | var bm = BitmapSource.Create(_mat.Cols, _mat.Rows, 96, 96, PixelFormats.Bgr24, null, _matData, _mat.Cols * _mat.NumberOfChannels); 76 | TheCanvas.Image = bm; 77 | TheCanvas.InvalidateVisual(); 78 | } 79 | 80 | private void SendKey() { 81 | if (IsActive) { 82 | return; 83 | } 84 | 85 | if (_sensors.All(s => !s.StateChanged)) { 86 | return; 87 | } 88 | 89 | switch (_config.SendKeyMode) { 90 | case "be": { 91 | var airKeys = String.Concat(from sensor in _sensors select sensor.Active ? "1" : "0"); 92 | _http.GetAsync(_config.EndPoint + "?k=" + airKeys); 93 | break; 94 | } 95 | case "chuni_io": { 96 | ChuniIO.Send(_sensors); 97 | break; 98 | } 99 | default: 100 | throw new Exception("unknown SendKeyMode"); 101 | } 102 | } 103 | 104 | private void Window_Loaded(object sender, RoutedEventArgs e) { 105 | StartCapture(); 106 | } 107 | 108 | private void RefreshCameras() { 109 | var cameras = CameraHelper.CameraHelper.GetCameras(); 110 | CameraCombo.Items.Clear(); 111 | foreach (var cam in cameras) { 112 | CameraCombo.Items.Add($"[{cam.Id}] {cam.Name}"); 113 | } 114 | 115 | if (_config.CameraId >= 0 && _config.CameraId < CameraCombo.Items.Count) { 116 | CameraCombo.SelectedIndex = _config.CameraId; 117 | } 118 | else { 119 | _config.CameraId = 0; 120 | } 121 | } 122 | 123 | private void StartCapture() { 124 | var cap = new VideoCapture(_config.CameraId, VideoCapture.API.DShow); 125 | if (!cap.IsOpened) { 126 | Logger.Error("Failed to start video capture"); 127 | return; 128 | } 129 | 130 | Logger.Info("Video capture started"); 131 | cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, _config.CaptureWidth); 132 | cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, _config.CaptureHeight); 133 | cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Autofocus, 0); 134 | cap.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.Exposure, _config.Exposure); 135 | 136 | _capture = cap; 137 | _capture.Read(_mat); 138 | _config.CaptureWidth = _mat.Cols; 139 | _config.CaptureHeight = _mat.Rows; 140 | 141 | _captureTask = Task.Run(CaptureLoop); 142 | } 143 | 144 | private void StopCapture() { 145 | Logger.Info("Stopping capture"); 146 | 147 | _closing = true; 148 | _captureTask?.Wait(); 149 | _captureTask = null; 150 | 151 | _capture?.Stop(); 152 | _capture?.Dispose(); 153 | _capture = null; 154 | 155 | _closing = false; 156 | } 157 | 158 | private void CaptureLoop() { 159 | // give camera some time to auto adjust, so user don't need to press reset right after start 160 | var bootstrapFrames = _config.BootstrapSeconds * _config.Fps; 161 | 162 | while (!_closing) { 163 | if (bootstrapFrames > 0) { 164 | _capture.Read(_mat); 165 | --bootstrapFrames; 166 | } 167 | else { 168 | if (!_config.FreezeVideo) { 169 | _capture.Read(_mat); 170 | } 171 | 172 | Dispatcher?.BeginInvoke(new Action(FrameUpdate)); 173 | } 174 | 175 | // what the...?? well, it works 176 | Thread.Sleep(1000 / _config.Fps); 177 | } 178 | } 179 | 180 | private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { 181 | StopCapture(); 182 | Helpers.Serialize(_config, ConfigFile); 183 | } 184 | 185 | private void ResetButton_Click(object sender, RoutedEventArgs e) { 186 | _hasPendingReset = true; 187 | } 188 | 189 | private void SetThresholdButton_Click(object sender, RoutedEventArgs e) { 190 | if (Double.TryParse(ThresholdBox.Text, out var v)) { 191 | _config.Threshold = v; 192 | Logger.Info($"Threshold = {v}"); 193 | } 194 | else { 195 | Logger.Error("Invalid input"); 196 | } 197 | } 198 | 199 | private void CenterButton_Click(object sender, RoutedEventArgs e) { 200 | _config.OffsetX = 0; 201 | _config.OffsetY = 0; 202 | } 203 | 204 | private void SetCameraBtn_Click(object sender, RoutedEventArgs e) { 205 | StopCapture(); 206 | 207 | _config.CameraId = CameraCombo.SelectedIndex; 208 | StartCapture(); 209 | } 210 | 211 | private void RefreshCameraBtn_Click(object sender, RoutedEventArgs e) { 212 | RefreshCameras(); 213 | } 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /chuni-hands/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("chuni-hands")] 9 | [assembly: AssemblyDescription("chuni sensor simulator")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("chuni-hands")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | //In order to begin building localizable applications, set 23 | //CultureYouAreCodingWith in your .csproj file 24 | //inside a . For example, if you are using US english 25 | //in your source files, set the to en-US. Then uncomment 26 | //the NeutralResourceLanguage attribute below. Update the "en-US" in 27 | //the line below to match the UICulture setting in the project file. 28 | 29 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 30 | 31 | 32 | [assembly: ThemeInfo( 33 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 34 | //(used if a resource is not found in the page, 35 | // or application resource dictionaries) 36 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 37 | //(used if a resource is not found in the page, 38 | // app, or any theme specific resource dictionaries) 39 | )] 40 | 41 | 42 | // Version information for an assembly consists of the following four values: 43 | // 44 | // Major Version 45 | // Minor Version 46 | // Build Number 47 | // Revision 48 | // 49 | // You can specify all the values or you can default the Build and Revision Numbers 50 | // by using the '*' as shown below: 51 | // [assembly: AssemblyVersion("1.0.*")] 52 | [assembly: AssemblyVersion("0.4.0.0")] 53 | [assembly: AssemblyFileVersion("0.4.0.0")] 54 | -------------------------------------------------------------------------------- /chuni-hands/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace chuni_hands.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("chuni_hands.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /chuni-hands/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /chuni-hands/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace chuni_hands.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /chuni-hands/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /chuni-hands/Sensor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Emgu.CV; 3 | using Emgu.CV.Structure; 4 | 5 | namespace chuni_hands { 6 | internal sealed class Sensor { 7 | public int X { get; private set; } 8 | public int Y { get; private set; } 9 | public bool Active { get; private set; } 10 | public bool StateChanged { get; private set; } 11 | public int Size => _config.SensorSize; 12 | public int Id => _id; 13 | 14 | private Mat _startValue; 15 | private readonly Config _config; 16 | private readonly int _id; 17 | 18 | public Sensor(int id, Config config) { 19 | _id = id; 20 | _config = config; 21 | } 22 | 23 | private Mat GetPartial(Mat frame) { 24 | var sz = _config.SensorSize; 25 | return new Mat(frame, new Range(Y - sz / 2, Y + sz / 2 + 1), new Range(X - sz / 2, X + sz / 2 + 1)); 26 | } 27 | 28 | public void Update(Mat frame, bool forceInit) { 29 | 30 | // reposition 31 | 32 | X = _config.CaptureWidth / 2 + _config.OffsetX; 33 | X = Math.Max(Math.Min(X, _config.CaptureWidth - _config.SensorSize), _config.SensorSize); 34 | 35 | Y = _config.CaptureHeight - (_config.CaptureHeight / 2 + _config.OffsetY + _id * _config.Distance); 36 | Y = Math.Max(Math.Min(Y, _config.CaptureHeight - _config.SensorSize), _config.SensorSize); 37 | 38 | // check area 39 | 40 | var pixels = GetPartial(frame); 41 | 42 | if (_startValue == null || forceInit) { 43 | _startValue = new Mat(pixels.Size, Emgu.CV.CvEnum.DepthType.Cv64F, pixels.NumberOfChannels); 44 | pixels.ConvertTo(_startValue, _startValue.Depth); 45 | _startValue /= 255; 46 | 47 | Active = false; 48 | StateChanged = false; 49 | return; 50 | } 51 | 52 | var pixelsD = new Mat(pixels.Size, Emgu.CV.CvEnum.DepthType.Cv64F, pixels.NumberOfChannels); 53 | pixels.ConvertTo(pixelsD, pixelsD.Depth); 54 | pixelsD /= 255; 55 | 56 | var matDiff = pixelsD - _startValue; 57 | var diff = matDiff.Dot(matDiff); 58 | if (_id == 0 && _config.LogDiff) { 59 | Logger.Info($"diff: {diff}"); 60 | } 61 | 62 | var active = diff > _config.Threshold; 63 | StateChanged = active != Active; 64 | Active = active; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /chuni-hands/Styles.xaml: -------------------------------------------------------------------------------- 1 |  4 | 7 | 10 | -------------------------------------------------------------------------------- /chuni-hands/chuni-hands.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F} 8 | WinExe 9 | chuni_hands 10 | chuni-hands 11 | v4.7.2 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | 17 | 18 | 19 | AnyCPU 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | true 39 | bin\x64\Debug\ 40 | DEBUG;TRACE 41 | full 42 | x64 43 | 7.3 44 | prompt 45 | MinimumRecommendedRules.ruleset 46 | true 47 | 48 | 49 | bin\x64\Release\ 50 | TRACE 51 | true 52 | pdbonly 53 | x64 54 | 7.3 55 | prompt 56 | MinimumRecommendedRules.ruleset 57 | true 58 | 59 | 60 | 61 | ..\packages\Lib.Harmony.1.2.0.1\lib\net472\0Harmony.dll 62 | 63 | 64 | C:\Emgu\emgucv-windesktop 4.2.0.3662\bin\Emgu.CV.World.Netstandard.dll 65 | 66 | 67 | ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 4.0 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | MSBuild:Compile 89 | Designer 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | MSBuild:Compile 99 | Designer 100 | 101 | 102 | App.xaml 103 | Code 104 | 105 | 106 | MainWindow.xaml 107 | Code 108 | 109 | 110 | Designer 111 | MSBuild:Compile 112 | 113 | 114 | 115 | 116 | Code 117 | 118 | 119 | True 120 | True 121 | Resources.resx 122 | 123 | 124 | True 125 | Settings.settings 126 | True 127 | 128 | 129 | ResXFileCodeGenerator 130 | Resources.Designer.cs 131 | 132 | 133 | 134 | SettingsSingleFileGenerator 135 | Settings.Designer.cs 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | {720d3af0-6a71-422b-8a93-1deece9e330f} 144 | CameraHelper 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /chuni-hands/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------