You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

643 lines
28 KiB

//----------------------------------------------------------------------------
// Copyright (C) 2004-2024 by EMGU Corporation. All rights reserved.
//----------------------------------------------------------------------------
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Emgu.CV.Structure;
using Emgu.Util;
using Platform = Emgu.Util.Platform;
namespace Emgu.CV
{
/// <summary>
/// Class that provide access to native OpenCV functions
/// </summary>
public static partial class CvInvoke
{
private static readonly bool _libraryLoaded;
/// <summary>
/// Check to make sure all the unmanaged libraries are loaded
/// </summary>
/// <returns>True if library loaded</returns>
public static bool Init()
{
return _libraryLoaded;
}
/// <summary>
/// string marshaling type
/// </summary>
public const UnmanagedType StringMarshalType = UnmanagedType.LPStr;
/// <summary>
/// Represent a bool value in C++
/// </summary>
public const UnmanagedType BoolMarshalType = UnmanagedType.U1;
/// <summary>
/// Represent a int value in C++
/// </summary>
public const UnmanagedType BoolToIntMarshalType = UnmanagedType.Bool;
/// <summary>
/// Opencv's calling convention
/// </summary>
public const CallingConvention CvCallingConvention = CallingConvention.Cdecl;
private static List<String> FindValidSubfolders(String baseFolder, List<String> subfolderOptions)
{
List<String> subfolderCandidates = new List<string>();
foreach (String sfo in subfolderOptions)
{
String combinedPath = Path.Combine(baseFolder, sfo);
if (Directory.Exists(combinedPath))
{
subfolderCandidates.Add(sfo);
}
}
return subfolderCandidates;
}
/// <summary>
/// Attempts to load opencv modules from the specific location
/// </summary>
/// <param name="loadDirectory">The directory where the unmanaged modules will be loaded. If it is null, the default location will be used.</param>
/// <param name="unmanagedModules">The names of opencv modules. e.g. "opencv_core.dll" on windows.</param>
/// <returns>True if all the modules has been loaded successfully</returns>
/// <remarks>If <paramref name="loadDirectory"/> is null, the default location on windows is the dll's path appended by either "x64" or "x86", depends on the applications current mode.</remarks>
public static bool LoadUnmanagedModules(String loadDirectory, params String[] unmanagedModules)
{
#if UNITY_WSA || UNITY_STANDALONE || UNITY_EDITOR || UNITY_ANDROID || UNITY_IOS || UNITY_WEBGL
if (loadDirectory != null)
{
throw new NotImplementedException("Loading modules from a specific directory is not implemented on the current platform");
}
//Let unity handle the library loading
return true;
#else
List<String> loadDirectories = new List<String>();
if (loadDirectory == null)
{
List<String> subfolderOptions = new List<string>();
if (Platform.OperationSystem == Emgu.Util.Platform.OS.MacOS)
{
subfolderOptions.Add(Path.Combine("runtimes", "osx", "native"));
}
else if (Platform.OperationSystem == Emgu.Util.Platform.OS.Linux)
{
switch (RuntimeInformation.ProcessArchitecture)
{
case Architecture.X86:
subfolderOptions.Add(Path.Combine("runtimes", "linux-x86", "native"));
subfolderOptions.Add(Path.Combine("runtimes", "ubuntu-x86", "native"));
subfolderOptions.Add("x86");
break;
case Architecture.X64:
subfolderOptions.Add(Path.Combine("runtimes", "linux-x64", "native"));
subfolderOptions.Add(Path.Combine("runtimes", "ubuntu-x64", "native"));
subfolderOptions.Add("x64");
break;
case Architecture.Arm:
subfolderOptions.Add(Path.Combine("runtimes", "linux-arm", "native"));
subfolderOptions.Add(Path.Combine("runtimes", "ubuntu-arm", "native"));
subfolderOptions.Add("arm");
break;
case Architecture.Arm64:
subfolderOptions.Add(Path.Combine("runtimes", "linux-arm64", "native"));
subfolderOptions.Add(Path.Combine("runtimes", "ubuntu-arm64", "native"));
subfolderOptions.Add("arm64");
break;
}
}
else if (Platform.OperationSystem == Emgu.Util.Platform.OS.Windows)
{
String existingDllDirectory = Emgu.Util.Toolbox.GetDllDirectory();
if (existingDllDirectory != null)
subfolderOptions.Add(existingDllDirectory);
switch (RuntimeInformation.ProcessArchitecture)
{
case Architecture.X86:
subfolderOptions.Add(Path.Combine("runtimes", "win-x86", "native"));
subfolderOptions.Add("x86");
break;
case Architecture.X64:
subfolderOptions.Add(Path.Combine("runtimes", "win-x64", "native"));
subfolderOptions.Add("x64");
break;
case Architecture.Arm:
subfolderOptions.Add(Path.Combine("runtimes", "win-arm", "native"));
subfolderOptions.Add("arm");
break;
case Architecture.Arm64:
subfolderOptions.Add(Path.Combine("runtimes", "win-arm64", "native"));
subfolderOptions.Add("arm64");
break;
}
}
List<String> subfolders = new List<String>();
System.Reflection.Assembly asm = typeof(CvInvoke).Assembly; //System.Reflection.Assembly.GetExecutingAssembly();
if ((String.IsNullOrEmpty(asm.Location) || !File.Exists(asm.Location)))
{
if (String.IsNullOrEmpty(AppDomain.CurrentDomain.BaseDirectory))
{
loadDirectory = String.Empty;
}
else
{
//we may be running in a debugger visualizer under a unit test in this case
String baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
DirectoryInfo baseDirectoryInfo = new DirectoryInfo(baseDirectory);
String debuggerVisualizerPath = String.Empty;
if (baseDirectoryInfo.Parent != null)
debuggerVisualizerPath = Path.Combine(baseDirectoryInfo.Parent.FullName, "Packages", "Debugger", "Visualizers");
if (!debuggerVisualizerPath.Equals(String.Empty) && Directory.Exists(debuggerVisualizerPath))
{
loadDirectory = debuggerVisualizerPath;
}
else
{
loadDirectory = baseDirectoryInfo.FullName;
}
subfolders = FindValidSubfolders(loadDirectory, subfolderOptions);
}
}
else
{
loadDirectory = Path.GetDirectoryName(asm.Location);
if (loadDirectory != null)
{
subfolders = FindValidSubfolders(loadDirectory, subfolderOptions);
}
}
if (subfolders.Count > 0)
{
foreach (var subfolder in subfolders)
{
if (Directory.Exists(Path.Combine(loadDirectory, subfolder)))
{
loadDirectories.Add(Path.Combine(loadDirectory, subfolder));
}
else
{
loadDirectories.Add(Path.Combine(Path.GetFullPath("."), subfolder));
}
}
}
else
{
loadDirectories.Add(loadDirectory);
}
if (!Directory.Exists(loadDirectory))
{
//try to find an alternative loadDirectory path
//The following code should handle finding the asp.NET BIN folder
if (String.IsNullOrEmpty(asm.Location) || !File.Exists(asm.Location))
{
System.Diagnostics.Trace.WriteLine(String.Format("asm.Location is invalid: '{0}'", asm.Location));
}
else
{
FileInfo file = new FileInfo(asm.Location);
DirectoryInfo directory = file.Directory;
foreach (var subfolder in subfolders)
{
if ((directory != null) && (!String.IsNullOrEmpty(subfolder)) &&
Directory.Exists(Path.Combine(directory.FullName, subfolder)))
{
loadDirectories.Add(Path.Combine(directory.FullName, subfolder));
}
else if (directory != null && Directory.Exists(directory.FullName))
{
loadDirectories.Add(directory.FullName);
}
}
}
}
}
else
{
loadDirectories.Add(loadDirectory);
}
foreach (String dir in loadDirectories)
{
if (TryLoadUnmanagedModulesFromDirectory(dir, unmanagedModules))
{
return true;
}
}
return false;
#endif
}
private static bool TryLoadUnmanagedModulesFromDirectory(string loadDirectory, string[] unmanagedModules)
{
String oldDir = String.Empty;
bool setDllDirectorySuccess = false;
if (!String.IsNullOrEmpty(loadDirectory) && Directory.Exists(loadDirectory))
{
if (Platform.ClrType == Platform.Clr.DotNetNative )
{
//do nothing
}
else if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.Windows )
{
//addDllDirectorySuccess = Emgu.Util.Toolbox.AddDllDirectory(loadDirectory);
setDllDirectorySuccess = Emgu.Util.Toolbox.SetDllDirectory(loadDirectory);
if (!setDllDirectorySuccess)
{
System.Diagnostics.Trace.WriteLine(String.Format("Failed to set dll directory: {0}", loadDirectory));
}
} else if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.IOS)
{
//do nothing
System.Diagnostics.Trace.WriteLine("iOS required static linking, Setting loadDirectory is not supported");
}
else
{
oldDir = Environment.CurrentDirectory;
Environment.CurrentDirectory = loadDirectory;
}
}
if (setDllDirectorySuccess)
{
System.Diagnostics.Trace.WriteLine(
String.Format(
"Loading Open CV binary from default locations. Current directory: {0}; Additional load folder: {1}",
Environment.CurrentDirectory,
loadDirectory));
}
else
{
System.Diagnostics.Trace.WriteLine(
String.Format(
"Loading Open CV binary from default locations. Current directory: {0}",
Environment.CurrentDirectory));
}
bool success = true;
string prefix = string.Empty;
foreach (String module in unmanagedModules)
{
string mName = module;
//handle special case for universal build
if (
mName.StartsWith("opencv_videoio_ffmpeg") //opencv_ffmpegvvv(_64).dll
&& (IntPtr.Size == 4) //32bit application
)
{
mName = module.Replace("_64", String.Empty);
}
bool optionalComponent = mName.Contains("ffmpeg");
String moduleName = Path.Combine(prefix, mName);
//Use absolute path for Windows Desktop
String fullPath = Path.Combine(loadDirectory, moduleName);
bool fileExist = File.Exists(fullPath);
bool loaded = false;
if (fileExist)
{
//Try to load using the full path
System.Diagnostics.Trace.WriteLine(String.Format("Found full path {0} for {1}. Trying to load it.", fullPath, mName));
loaded = !IntPtr.Zero.Equals(Toolbox.LoadLibrary(fullPath));
if (loaded)
System.Diagnostics.Trace.WriteLine(String.Format("{0} loaded.", mName));
else
System.Diagnostics.Trace.WriteLine(String.Format("Failed to load {0} from {1}.", mName, fullPath));
}
if (!loaded)
{
//Try to load without the full path
System.Diagnostics.Trace.WriteLine(String.Format("Trying to load {0} using default path.", mName));
loaded = !IntPtr.Zero.Equals(Toolbox.LoadLibrary(mName));
if (loaded)
System.Diagnostics.Trace.WriteLine(String.Format("{0} loaded using default path", mName));
else
System.Diagnostics.Trace.WriteLine(String.Format("Failed to load {0} using default path", mName));
}
if (!loaded)
System.Diagnostics.Trace.WriteLine(String.Format("!!! Failed to load {0}.", mName));
if (!optionalComponent)
success &= loaded;
}
if (!oldDir.Equals(String.Empty))
{
Environment.CurrentDirectory = oldDir;
}
return success;
}
/// <summary>
/// Get the module format string.
/// </summary>
/// <returns>On Windows, "{0}".dll will be returned; On Linux, "lib{0}.so" will be returned; Otherwise {0} is returned.</returns>
public static String GetModuleFormatString()
{
String formatString = "{0}";
if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.Windows)
formatString = "{0}.dll";
else if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.Linux)
formatString = "lib{0}.so";
else if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.MacOS)
formatString = "lib{0}.dylib";
return formatString;
}
/// <summary>
/// Attempts to load opencv modules from the specific location
/// </summary>
/// <param name="modules">The names of opencv modules. e.g. "opencv_core.dll" on windows.</param>
/// <param name="loadDirectory">The path to load the opencv modules. If null, will use the default path.</param>
/// <returns>True if all the modules has been loaded successfully</returns>
public static bool DefaultLoadUnmanagedModules(String[] modules, String loadDirectory = null)
{
bool libraryLoaded = true;
#if !(UNITY_ANDROID || UNITY_IOS || UNITY_EDITOR || UNITY_STANDALONE || UNITY_WSA || UNITY_WEBGL)
if (Emgu.Util.Platform.OperationSystem == Platform.OS.IOS)
return libraryLoaded;
else if (Emgu.Util.Platform.OperationSystem == Platform.OS.Android && (Emgu.Util.Platform.ClrType != Platform.Clr.Unity))
{
System.Reflection.Assembly monoAndroidAssembly = Emgu.Util.Toolbox.FindAssembly("Mono.Android.dll");
//Running on Xamarin Android
Type javaSystemType = monoAndroidAssembly.GetType("Java.Lang.JavaSystem");
if (javaSystemType != null)
{
System.Reflection.MethodInfo loadLibraryMethodInfo = javaSystemType.GetMethod("LoadLibrary");
if (loadLibraryMethodInfo != null)
{
foreach (String module in modules)
{
if (module.StartsWith("opencv_videoio_ffmpeg"))
continue; //skip the ffmpeg modules.
try
{
String fullModulePath;
if (loadDirectory != null)
{
fullModulePath = Path.Combine(loadDirectory, module);
}
else
{
fullModulePath = module;
}
System.Diagnostics.Trace.WriteLine(string.Format("Trying to load {0} ({1} bit).", fullModulePath,
IntPtr.Size * 8));
loadLibraryMethodInfo.Invoke(null, new object[] { fullModulePath });
//Java.Lang.JavaSystem.LoadLibrary(module);
System.Diagnostics.Trace.WriteLine(string.Format("Loaded {0}.", fullModulePath));
}
catch (Exception e)
{
libraryLoaded = false;
System.Diagnostics.Trace.WriteLine(String.Format("Failed to load {0}: {1}", module, e.Message));
if (e.InnerException != null && e.InnerException.Message != null)
{
System.Diagnostics.Trace.WriteLine(String.Format("InnerException {0}", e.InnerException.Message));
}
}
}
return libraryLoaded;
}
}
}
else //if (Emgu.Util.Platform.OperationSystem != Emgu.Util.Platform.OS.MacOS)
{
String formatString = GetModuleFormatString();
for (int i = 0; i < modules.Length; ++i)
modules[i] = String.Format(formatString, modules[i]);
libraryLoaded &= LoadUnmanagedModules(loadDirectory, modules);
}
#endif
return libraryLoaded;
}
/// <summary>
/// Static Constructor to setup opencv environment
/// </summary>
static CvInvoke()
{
#if ( UNITY_WEBGL && ! UNITY_EDITOR )
_libraryLoaded = true;
#else
if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.IOS ||
Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.MacCatalyst)
{
/*
Assembly assembly = Assembly.GetExecutingAssembly();
System.Runtime.InteropServices.DllImportResolver resolver =
(string libraryName, Assembly asm, DllImportSearchPath? dllImportSearchPath) =>
{
if (dllImportSearchPath != DllImportSearchPath.System32)
{
Console.WriteLine($"Unexpected dllImportSearchPath: {dllImportSearchPath.ToString()}");
throw new ArgumentException();
}
return System.Runtime.InteropServices.NativeLibrary.Load("ResolveLib", asm, null);
};
*/
//iOS or MacCatalyst libraries are static linked, assume correct loading by default.
_libraryLoaded = true;
return;
}
else
{
List<String> modules = CvInvoke.OpenCVModuleList;
modules.RemoveAll(String.IsNullOrEmpty);
_libraryLoaded = DefaultLoadUnmanagedModules(modules.ToArray());
try
{
//Use the custom error handler
RedirectError(CvErrorHandlerThrowException, IntPtr.Zero, IntPtr.Zero);
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine(
String.Format("Failed to register error handler using RedirectError : {0}", e.StackTrace));
throw;
}
}
#endif
if (Emgu.Util.Platform.OperationSystem == Emgu.Util.Platform.OS.MacOS)
{
//Need to set the correct Open CV temp path to avoid error
String opencv_temp_path =
Environment.GetEnvironmentVariable("OPENCV_TEMP_PATH", EnvironmentVariableTarget.Process);
if (String.IsNullOrEmpty(opencv_temp_path))
{
//If the path is not set, set it to system temp path
Environment.SetEnvironmentVariable("OPENCV_TEMP_PATH", System.IO.Path.GetTempPath(), EnvironmentVariableTarget.Process);
opencv_temp_path = Environment.GetEnvironmentVariable("OPENCV_TEMP_PATH", EnvironmentVariableTarget.Process);
}
//Create the opencv folder in the temp path
String opencv_temp_folder = Path.Combine(opencv_temp_path, "opencv");
if (!Directory.Exists(opencv_temp_folder))
Directory.CreateDirectory(opencv_temp_folder);
}
}
#region CV MACROS
/// <summary>
/// Get the corresponding depth type
/// </summary>
/// <param name="t">The opencv depth type</param>
/// <returns>The equivalent depth type</returns>
public static Type GetDepthType(CvEnum.DepthType t)
{
switch (t)
{
case CvEnum.DepthType.Cv8U:
return typeof(byte);
case CvEnum.DepthType.Cv8S:
return typeof(SByte);
case CvEnum.DepthType.Cv16U:
return typeof(UInt16);
case CvEnum.DepthType.Cv16S:
return typeof(Int16);
case CvEnum.DepthType.Cv32S:
return typeof(Int32);
case CvEnum.DepthType.Cv32F:
return typeof(float);
case CvEnum.DepthType.Cv64F:
return typeof(double);
default:
throw new ArgumentException(String.Format("Unable to convert type {0} to depth type", t.ToString()));
}
}
/// <summary>
/// Get the corresponding opencv depth type
/// </summary>
/// <param name="t">The element type</param>
/// <returns>The equivalent opencv depth type</returns>
public static CvEnum.DepthType GetDepthType(Type t)
{
if (t == typeof(byte))
{
return CvEnum.DepthType.Cv8U;
}
else if (t == typeof(SByte))
{
return CvEnum.DepthType.Cv8S;
}
else if (t == typeof(UInt16))
{
return CvEnum.DepthType.Cv16U;
}
else if (t == typeof(Int16))
{
return CvEnum.DepthType.Cv16S;
}
else if (t == typeof(Int32))
{
return CvEnum.DepthType.Cv32S;
}
else if (t == typeof(float))
{
return CvEnum.DepthType.Cv32F;
}
else if (t == typeof(double))
{
return CvEnum.DepthType.Cv64F;
}
else
{
throw new ArgumentException(String.Format("Unable to convert type {0} to depth type", t.ToString()));
}
}
/// <summary>
/// This function performs the same as MakeType macro
/// </summary>
/// <param name="depth">The type of depth</param>
/// <param name="channels">The number of channels</param>
/// <returns>An integer that represent a mat type</returns>
public static int MakeType(CvEnum.DepthType depth, int channels)
{
const int shift = 3;
return (((int)depth) & ((1 << shift) - 1)) + (((channels) - 1) << shift);
}
/*
/// <summary>
/// Generate 4-character code of codec used to compress the frames. For example, CV_FOURCC('P','I','M','1') is MPEG-1 codec, CV_FOURCC('M','J','P','G') is motion-jpeg codec etc.
/// </summary>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <param name="c3"></param>
/// <param name="c4"></param>
/// <returns></returns>
public static int CV_FOURCC(char c1, char c2, char c3, char c4)
{
return (((c1) & 255) + (((c2) & 255) << 8) + (((c3) & 255) << 16) + (((c4) & 255) << 24));
}*/
#endregion
/// <summary>
/// Check if the size of the C structures match those of C#
/// </summary>
/// <returns>True if the size matches</returns>
public static bool SanityCheck()
{
bool sane = true;
CvStructSizes sizes = CvInvoke.GetCvStructSizes();
sane &= (sizes.CvBox2D == Toolbox.SizeOf<RotatedRect>());
sane &= (sizes.CvMat == Toolbox.SizeOf<MCvMat>());
sane &= (sizes.CvMatND == Toolbox.SizeOf<MCvMatND>());
sane &= (sizes.CvPoint == Toolbox.SizeOf<System.Drawing.Point>());
sane &= (sizes.CvPoint2D32f == Toolbox.SizeOf<System.Drawing.PointF>());
sane &= (sizes.CvPoint3D32f == Toolbox.SizeOf<MCvPoint3D32f>());
sane &= (sizes.CvRect == Toolbox.SizeOf<System.Drawing.Rectangle>());
sane &= (sizes.CvScalar == Toolbox.SizeOf<MCvScalar>());
sane &= (sizes.CvSize == Toolbox.SizeOf<System.Drawing.Size>());
sane &= (sizes.CvSize2D32f == Toolbox.SizeOf<System.Drawing.SizeF>());
sane &= (sizes.CvTermCriteria == Toolbox.SizeOf<MCvTermCriteria>());
sane &= 2 * Toolbox.SizeOf<int>() == Toolbox.SizeOf<Emgu.CV.Structure.Range>();
int miplImageSize = Toolbox.SizeOf<MIplImage>();
sane &= (sizes.IplImage == miplImageSize);
return sane;
}
}
}