diff --git a/CameraHelper/AssemblyInfo.cpp b/CameraHelper/AssemblyInfo.cpp new file mode 100644 index 0000000..9c796b5 --- /dev/null +++ b/CameraHelper/AssemblyInfo.cpp @@ -0,0 +1,22 @@ +#include "pch.h" + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +[assembly:AssemblyTitleAttribute(L"CameraHelper")]; +[assembly:AssemblyDescriptionAttribute(L"")]; +[assembly:AssemblyConfigurationAttribute(L"")]; +[assembly:AssemblyCompanyAttribute(L"")]; +[assembly:AssemblyProductAttribute(L"CameraHelper")]; +[assembly:AssemblyCopyrightAttribute(L"Copyright (c) 2020")]; +[assembly:AssemblyTrademarkAttribute(L"")]; +[assembly:AssemblyCultureAttribute(L"")]; + +[assembly:AssemblyVersionAttribute("1.0.*")]; + +[assembly:ComVisible(false)]; + +[assembly:CLSCompliantAttribute(true)]; diff --git a/CameraHelper/CameraHelper.cpp b/CameraHelper/CameraHelper.cpp new file mode 100644 index 0000000..3e3c2c4 --- /dev/null +++ b/CameraHelper/CameraHelper.cpp @@ -0,0 +1,56 @@ +#include "pch.h" + +#include +#include +#include + +#include "CameraHelper.h" + + +using namespace System; +using namespace System::Collections::Generic; +using namespace CameraHelper; + +List^ ::CameraHelper::CameraHelper::GetCameras() { + auto list = gcnew List; + + HRESULT hr; + ICreateDevEnum* pSysDevEnum; + hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, + IID_ICreateDevEnum, (void**)&pSysDevEnum); + if (FAILED(hr)) { + return list; + } + + IEnumMoniker *pEnumCat; + hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnumCat, 0); + if (hr != S_OK) { + pSysDevEnum->Release(); + return list; + } + + IMoniker *pMoniker; + ULONG cFetched; + auto id = 0; + while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK) { + IPropertyBag* pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pPropBag); + if (SUCCEEDED(hr)) { + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if (SUCCEEDED(hr)) { + auto info = gcnew CameraInfo; + info->Id = id++; + info->Name = gcnew String(varName.bstrVal); + list->Add(info); + } + VariantClear(&varName); + pPropBag->Release(); + } + pMoniker->Release(); + } + pEnumCat->Release(); + + return list; +} \ No newline at end of file diff --git a/CameraHelper/CameraHelper.h b/CameraHelper/CameraHelper.h new file mode 100644 index 0000000..279ec22 --- /dev/null +++ b/CameraHelper/CameraHelper.h @@ -0,0 +1,16 @@ +#pragma once + +using namespace System; + +namespace CameraHelper { + public ref class CameraInfo { + public: + property int Id; + property String^ Name; + }; + + public ref class CameraHelper { + public: + static System::Collections::Generic::List^ GetCameras(); + }; +} diff --git a/CameraHelper/CameraHelper.vcxproj b/CameraHelper/CameraHelper.vcxproj new file mode 100644 index 0000000..c44785f --- /dev/null +++ b/CameraHelper/CameraHelper.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F} + v4.5 + ManagedCProj + CameraHelper + 10.0 + + + + DynamicLibrary + true + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + DynamicLibrary + true + v142 + true + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + bin\$(Platform)\$(Configuration)\ + obj\$(Platform)\$(Configuration)\ + + + + Use + pch.h + Level3 + NDEBUG;%(PreprocessorDefinitions) + + + Strmiids.lib;ole32.lib;OleAut32.lib + + + + + Use + pch.h + Level3 + WIN32;_DEBUG;%(PreprocessorDefinitions) + + + + + + + + Use + pch.h + Level3 + _DEBUG;%(PreprocessorDefinitions) + + + Strmiids.lib;ole32.lib;OleAut32.lib + + + + + Use + pch.h + Level3 + WIN32;NDEBUG;%(PreprocessorDefinitions) + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CameraHelper/CameraHelper.vcxproj.filters b/CameraHelper/CameraHelper.vcxproj.filters new file mode 100644 index 0000000..a650ea7 --- /dev/null +++ b/CameraHelper/CameraHelper.vcxproj.filters @@ -0,0 +1,49 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/CameraHelper/CameraHelper.vcxproj.user b/CameraHelper/CameraHelper.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/CameraHelper/CameraHelper.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/CameraHelper/Resource.h b/CameraHelper/Resource.h new file mode 100644 index 0000000..d5ac7c4 --- /dev/null +++ b/CameraHelper/Resource.h @@ -0,0 +1,3 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by app.rc diff --git a/CameraHelper/app.ico b/CameraHelper/app.ico new file mode 100644 index 0000000..789d7cc Binary files /dev/null and b/CameraHelper/app.ico differ diff --git a/CameraHelper/app.rc b/CameraHelper/app.rc new file mode 100644 index 0000000..eab4306 Binary files /dev/null and b/CameraHelper/app.rc differ diff --git a/CameraHelper/pch.cpp b/CameraHelper/pch.cpp new file mode 100644 index 0000000..64b7eef --- /dev/null +++ b/CameraHelper/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/CameraHelper/pch.h b/CameraHelper/pch.h new file mode 100644 index 0000000..9d715b0 --- /dev/null +++ b/CameraHelper/pch.h @@ -0,0 +1,12 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +// add headers that you want to pre-compile here + +#endif //PCH_H diff --git a/CameraHelperTest/App.config b/CameraHelperTest/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/CameraHelperTest/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/CameraHelperTest/CameraHelperTest.csproj b/CameraHelperTest/CameraHelperTest.csproj new file mode 100644 index 0000000..c4193e1 --- /dev/null +++ b/CameraHelperTest/CameraHelperTest.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {13F60778-2DF6-4723-BB87-2A433B822307} + Exe + CameraHelperTest + CameraHelperTest + v4.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + + + + + + + + + + + + + + + + + + + + + {720d3af0-6a71-422b-8a93-1deece9e330f} + CameraHelper + + + + \ No newline at end of file diff --git a/CameraHelperTest/Program.cs b/CameraHelperTest/Program.cs new file mode 100644 index 0000000..7a8a9a9 --- /dev/null +++ b/CameraHelperTest/Program.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CameraHelperTest { + class Program { + static void Main(string[] args) { + var cameras = CameraHelper.CameraHelper.GetCameras(); + foreach (var camera in cameras) { + Console.WriteLine($"{camera.Id} {camera.Name}"); + } + + Console.ReadLine(); + } + } +} diff --git a/CameraHelperTest/Properties/AssemblyInfo.cs b/CameraHelperTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..da4d07f --- /dev/null +++ b/CameraHelperTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 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("CameraHelperTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CameraHelperTest")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[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)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("13f60778-2df6-4723-bb87-2a433b822307")] + +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/chuni-hands.sln b/chuni-hands.sln index b6c1ef4..b068dd5 100644 --- a/chuni-hands.sln +++ b/chuni-hands.sln @@ -7,6 +7,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "chuni-hands", "chuni-hands\ 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 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CameraHelper", "CameraHelper\CameraHelper.vcxproj", "{720D3AF0-6A71-422B-8A93-1DEECE9E330F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CameraHelperTest", "CameraHelperTest\CameraHelperTest.csproj", "{13F60778-2DF6-4723-BB87-2A433B822307}" +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 @@ -15,18 +19,46 @@ Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 + Release|x86 = Release|x86 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}.Debug|x86.ActiveCfg = Debug|x86 + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Debug|x86.Build.0 = Debug|x86 {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 + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x86.ActiveCfg = Release|x86 + {F1FDD6C6-7B45-403D-9A53-8E9CA600BC3F}.Release|x86.Build.0 = Release|x86 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x64.ActiveCfg = Debug|x64 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x64.Build.0 = Debug|x64 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x86.ActiveCfg = Debug|Win32 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Debug|x86.Build.0 = Debug|Win32 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|Any CPU.ActiveCfg = Release|Win32 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x64.ActiveCfg = Release|x64 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x64.Build.0 = Release|x64 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x86.ActiveCfg = Release|Win32 + {720D3AF0-6A71-422B-8A93-1DEECE9E330F}.Release|x86.Build.0 = Release|Win32 + {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x64.ActiveCfg = Debug|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x64.Build.0 = Debug|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x86.ActiveCfg = Debug|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Debug|x86.Build.0 = Debug|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Release|Any CPU.Build.0 = Release|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x64.ActiveCfg = Release|x64 + {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x64.Build.0 = Release|x64 + {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x86.ActiveCfg = Release|Any CPU + {13F60778-2DF6-4723-BB87-2A433B822307}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/chuni-hands/ChuniCanvas.cs b/chuni-hands/ChuniCanvas.cs index 9b94c22..b39cb3c 100644 --- a/chuni-hands/ChuniCanvas.cs +++ b/chuni-hands/ChuniCanvas.cs @@ -30,6 +30,7 @@ namespace chuni_hands { protected override void OnRender(DrawingContext dc) { base.OnRender(dc); + dc.DrawRectangle(Brushes.LightGray, null, new Rect(0, 0, ActualWidth, ActualHeight)); var image = Image; if (image == null) { return; @@ -44,9 +45,6 @@ namespace chuni_hands { if (DrawImage) { dc.DrawImage(image, imageRect); } - else { - dc.DrawRectangle(Brushes.LightGray, null, imageRect); - } DrawSensors(dc, factor, paddingX, paddingY); } diff --git a/chuni-hands/MainWindow.xaml b/chuni-hands/MainWindow.xaml index 6856c89..c425036 100644 --- a/chuni-hands/MainWindow.xaml +++ b/chuni-hands/MainWindow.xaml @@ -44,6 +44,13 @@ Y + + + + + + + Freeze Log diff diff --git a/chuni-hands/MainWindow.xaml.cs b/chuni-hands/MainWindow.xaml.cs index a52994a..1796b5d 100644 --- a/chuni-hands/MainWindow.xaml.cs +++ b/chuni-hands/MainWindow.xaml.cs @@ -16,7 +16,7 @@ namespace chuni_hands { private VideoCapture _capture; private readonly List _sensors = new List(6); - private readonly Mat _mat = new Mat(); + private Mat _mat = new Mat(); private byte[] _matData = new byte[0]; private bool _hasPendingReset = false; @@ -33,10 +33,15 @@ namespace chuni_hands { _config = Helpers.Deserialize(ConfigFile); } + for (var i = 0; i < 6; ++i) { + _sensors.Add(new Sensor(i, _config)); + } + InitializeComponent(); Title += " version " + Helpers.GetVersion(); TheCanvas.Sensors = _sensors; + RefreshCameras(); Logger.LogAdded += log => { LogBox.AppendText(log); @@ -97,7 +102,32 @@ namespace chuni_hands { } private void Window_Loaded(object sender, RoutedEventArgs e) { - var cap = new VideoCapture(_config.CameraId); + StartCapture(); + } + + private void RefreshCameras() { + var cameras = CameraHelper.CameraHelper.GetCameras(); + CameraCombo.Items.Clear(); + foreach (var cam in cameras) { + CameraCombo.Items.Add($"[{cam.Id}] {cam.Name}"); + } + + if (_config.CameraId >= 0 && _config.CameraId < CameraCombo.Items.Count) { + CameraCombo.SelectedIndex = _config.CameraId; + } + else { + _config.CameraId = 0; + } + } + + private void StartCapture() { + var cap = new VideoCapture(_config.CameraId, VideoCapture.API.DShow); + if (!cap.IsOpened) { + Logger.Error("Failed to start video capture"); + return; + } + + Logger.Info("Video capture started"); 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); @@ -108,13 +138,23 @@ namespace chuni_hands { _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 StopCapture() { + Logger.Info("Stopping capture"); + + _closing = true; + _captureTask?.Wait(); + _captureTask = null; + + _capture?.Stop(); + _capture?.Dispose(); + _capture = null; + + _closing = false; + } + private void CaptureLoop() { // give camera some time to auto adjust, so user don't need to press reset right after start var bootstrapFrames = _config.BootstrapSeconds * _config.Fps; @@ -138,12 +178,7 @@ namespace chuni_hands { } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { - _closing = true; - _captureTask.Wait(); - - _capture.Stop(); - _capture.Dispose(); - + StopCapture(); Helpers.Serialize(_config, ConfigFile); } @@ -165,5 +200,16 @@ namespace chuni_hands { _config.OffsetX = 0; _config.OffsetY = 0; } + + private void SetCameraBtn_Click(object sender, RoutedEventArgs e) { + StopCapture(); + + _config.CameraId = CameraCombo.SelectedIndex; + StartCapture(); + } + + private void RefreshCameraBtn_Click(object sender, RoutedEventArgs e) { + RefreshCameras(); + } } } diff --git a/chuni-hands/chuni-hands.csproj b/chuni-hands/chuni-hands.csproj index bf85180..894e4c4 100644 --- a/chuni-hands/chuni-hands.csproj +++ b/chuni-hands/chuni-hands.csproj @@ -138,6 +138,12 @@ + + + {720d3af0-6a71-422b-8a93-1deece9e330f} + CameraHelper + + \ No newline at end of file