initial commit

master 0.1.0.0
logchan 2020-02-12 22:09:43 -05:00
commit 775c623024
22 changed files with 1074 additions and 0 deletions

197
.editorconfig 100644
View File

@ -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

3
.gitignore vendored 100644
View File

@ -0,0 +1,3 @@
.vs/
bin/
obj/

14
README.md 100644
View File

@ -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)

37
chuni-hands.sln 100644
View File

@ -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

View File

@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=chuni/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup>
</configuration>

View File

@ -0,0 +1,9 @@
<Application x:Class="chuni_hands.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:chuni_hands"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,9 @@
using System.Windows;
namespace chuni_hands {
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application {
}
}

View File

@ -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<Sensor> 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));
}
}
}

View File

@ -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";
}
}

View File

@ -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<T>(string path) {
var content = File.ReadAllText(path);
return JsonConvert.DeserializeObject<T>(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;
}
}
}

View File

@ -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
}
}
}

View File

@ -0,0 +1,47 @@
<Window x:Class="chuni_hands.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:chuni_hands"
mc:Ignorable="d" x:Name="TheWindow"
Title="chuni-hands" Height="720" Width="1280"
Loaded="Window_Loaded" Closing="Window_Closing">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Margin="12">
<local:ChuniCanvas x:Name="TheCanvas"></local:ChuniCanvas>
</Grid>
<DockPanel Grid.Column="1" LastChildFill="True">
<StackPanel DockPanel.Dock="Top" Margin="12">
<Button x:Name="ResetButton" Click="ResetButton_Click">Reset</Button>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Left">Threshold</TextBlock>
<Button DockPanel.Dock="Right" x:Name="SetThresholdButton" Click="SetThresholdButton_Click">Set</Button>
<TextBox Text="{Binding ElementName=TheWindow, Path=Config.Threshold}" x:Name="ThresholdBox"></TextBox>
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Left">Distance </TextBlock>
<Slider DockPanel.Dock="Right" Value="{Binding ElementName=TheWindow, Path=Config.Distance, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Minimum="20" Maximum="100"></Slider>
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Left">X</TextBlock>
<Slider DockPanel.Dock="Right" Value="{Binding ElementName=TheWindow, Path=Config.OffsetX, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Minimum="-320" Maximum="320"></Slider>
</DockPanel>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Left">Y</TextBlock>
<Slider DockPanel.Dock="Right" Value="{Binding ElementName=TheWindow, Path=Config.OffsetY, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Minimum="-240" Maximum="240"></Slider>
</DockPanel>
<CheckBox IsChecked="{Binding ElementName=TheWindow, Path=Config.LogDiff, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Log diff</CheckBox>
</StackPanel>
<RichTextBox DockPanel.Dock="Bottom" x:Name="LogBox">
</RichTextBox>
</DockPanel>
</Grid>
</Window>

View File

@ -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<Sensor> _sensors = new List<Sensor>(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<Config>(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;
}
}
}
}

View File

@ -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
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> 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")]

View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace chuni_hands.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
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;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@ -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);
}
}
}
}
}

View File

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>chuni_hands</RootNamespace>
<AssemblyName>chuni-hands</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<Deterministic>true</Deterministic>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Emgu.CV.World.Netstandard">
<HintPath>C:\Emgu\emgucv-windesktop 4.2.0.3662\bin\Emgu.CV.World.Netstandard.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="ChuniCanvas.cs" />
<Compile Include="Config.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="Logger.cs" />
<Compile Include="Sensor.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="C:\Emgu\emgucv-windesktop 4.2.0.3662\Emgu.CV.Runtime\Windows\Emgu.CV.Runtime.Windows.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
</packages>