shimat / opencvsharp

OpenCV wrapper for .NET
Apache License 2.0
5.39k stars 1.15k forks source link

MacOS runtime error (Unable to load shared library 'OpenCvSharpExtern' or one of its dependencies) #949

Closed vlozhnikov closed 4 years ago

vlozhnikov commented 4 years ago

Summary of your issue

Hi I tried use OpenCVSharp in my MacOS F# project. I added OpenCvSharp4.runtime.osx.10-15-x64 in my solution

Снимок экрана 2020-05-15 в 16 08 09

Environment

MacOS 10.15.4, Visual Studio for Mac 8.5.6 (build 11)

Example code:

open OpenCvSharp

open System

[<EntryPoint>]
let main argv =
    let src = new Mat("cat.jpg", ImreadModes.Grayscale); // <<----- Exception is here
    // Mat src = Cv2.ImRead("lenna.png", ImreadModes.Grayscale);
    let dst = new Mat();

    let in1 = InputArray.Create(src)
    let in2 = OutputArray.Create(dst)

    Cv2.Canny(in1, in2, 50., 200.);
    use w1 = new Window("src image", src)
    use w2 = new Window("dst image", dst)

    Cv2.WaitKey() |> ignore

    0 // return an integer exit code

Output:

"Unable to load shared library 'OpenCvSharpExtern' or one of its dependencies. In order to help diagnose loading problems, consider setting the DYLD_PRINT_LIBRARIES environment variable: dlopen(libOpenCvSharpExtern, 1): image not found"

What did you intend to be?

shimat commented 4 years ago

Please check the dependencies of libOpenCvSharpExtern.dylib in runtime/osx-x64/native. https://github.com/shimat/opencvsharp/blob/master/.github/workflows/macos10.yml#L21 https://github.com/shimat/opencvsharp/blob/master/.github/workflows/macos10.yml#L56

vlozhnikov commented 4 years ago

hi i tried

brew install wget pkg-config mono-libgdiplus gtk+ ffmpeg glog yasm harfbuzz jpeg libpng libtiff openexr openjpeg metis openblas opencore-amr protobuf tbb webp

and then

otool -L libOpenCvSharpExtern.dylib

it says "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/objdump: error: 'libOpenCvSharpExtern.dylib': No such file or directory"

seems libOpenCvSharpExtern.dylib does't exists. how can i install it?

thanks

shimat commented 4 years ago

The OpenCvSharp4.runtime.osx.10-15-x64 NuGet package should place libOpenCvSharpExtern.dylib in the same directory as your executable file (.exe) or <exe directory>/runtimes/osx-x64/native/ . Does it exist? https://github.com/shimat/opencvsharp/blob/master/nuget/OpenCvSharp4.runtime.osx.10.15-x64.nuspec#L27

vlozhnikov commented 4 years ago

Unfortunatelly no

I'm trying to use OpenCvSharp in my DLL module. The following images show you structure of my project and files inside DLL module. My DLL module called OpenCVView

Снимок экрана 2020-05-18 в 12 19 53 Снимок экрана 2020-05-18 в 12 19 11

Thank you

shimat commented 4 years ago

According to the image, there is a runtimes directory in OpenCvView/bin/Debug/netcoreapp3.1. Is there a libOpenCvSharpExtern in it? (runtimes/osx-x64/native/libOpenCvSharpExtern.dylib) If so, please try copying OpenCvView/bin/Debug/netcoreapp3.1/runtimes/osx-x64/native/libOpenCvSharpExtern.dylib to OpenCvView/bin/Debug/netcoreapp3.1/libOpenCvSharpExtern.dylib . Also, make sure that your application is running on x64, not x86.

vlozhnikov commented 4 years ago

Yes, i found libOpenCvSharpExtern.dylib and copied to folder with my dll module It helped. Thank you very much!

ccaneke commented 4 years ago

I'm also on MacOs with exactly the same symptoms described here, but I've been stuck on this error for a while.

matthewrdev commented 3 years ago

For those struggling with this on MacOS, I managed to get this working by including the libOpenCvSharpExtern.dylib into the Mac project head as a BundleResource. This ensure that it is placed in the Resources folder of the final app bundle within the bin folder, making it accessible for the Mac app to load.

The libOpenCvSharpExtern.dylib can be found within the runtimes folder of the OpenCvSharp4.runtime.osx folder within your projects packages folder.

open-cv-dylib-location


Add dylib to project and set as BundleResource build-action

Open apps package contents show-package-contents

Verify dylib is within Resources dylib-bundle-resource

atkinsonbg commented 2 years ago

@vlozhnikov @shimat Could we re-open this one? I'm having the same issue and am not able to resolve it.

My Environment: MacOS Big Sur 11.6, Visual Studio Code v1.63.2, Visual Studio for Mac 2022 Preview v17.0 Build 5186

I've tried the following:

However, still getting the error:

Screen Shot 2022-01-03 at 11 28 27 AM

Any other thoughts?

atkinsonbg commented 2 years ago
Screen Shot 2022-01-03 at 11 40 00 AM

These are the only options I have for marking the file as Content.

matthewrdev commented 2 years ago

@atkinsonbg Since I posted this comment I've refined my approach a bit further.

First thing I've changed is to create a custom build targets to copy the dylib into the MonoBundle directory (where all macOS assemblies live).

1: Change the build action of the libOpenCvSharpExtern.dylib to None.

2: Create a new file in the root of the macOS project named libOpenCvSharpExtern.targets with a build action of None. Paste the following content into the file:

<!-- All msbuild target logic needs to be inside a project tag -->
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <!-- These targets will fire after mmp creates your bundle but before code signing -->
    <PropertyGroup>
        <CreateAppBundleDependsOn>$(CreateAppBundleDependsOn);BundleOpenCVAssets</CreateAppBundleDependsOn>
    </PropertyGroup>

    <!-- Since this has inputs/outputs, it will fire only when the inputs are changed or the output does not exist -->
    <Target Name="BundleOpenCVAssets" Inputs="libOpenCvSharpExtern.dylib" Outputs="$(AppBundleDir)/Contents/MonoBundle/libOpenCvSharpExtern.dylib">
        <Message Text="Copying 'libOpenCvSharpExtern.dylib' and 'OpenCvSharp.dll.config' into '$(AppBundleDir)/Contents/MonoBundle/...'" />
        <Copy SourceFiles="libOpenCvSharpExtern.dylib" DestinationFiles="$(AppBundleDir)/Contents/MonoBundle/libOpenCvSharpExtern.dylib" />
        <Copy SourceFiles="OpenCvSharp.dll.config" DestinationFiles="$(AppBundleDir)/Contents/MonoBundle/OpenCvSharp.dll.config" />
    </Target>
</Project>

3: Open the macOS project file and add the following line beneath the Xamarin.Mac.CSharp.targets import:

<Import Project="libOpenCvSharpExtern.targets" />

If the steps above do not work, you'll need to track down which dependencies as missing using the macOS otool -L against the libOpenCvSharpExtern.dylib:

otool -L libOpenCvSharpExtern.dylib

Typically we (myself and a freelancer who works with me) have found that OpenCV doesn't work when:

  1. ffmpeg is not installed. This can often be fixed by installing ffmpeg via home brew.
  2. ffmpeg is installed but is not in the expected path. We've seen this happen on Apple Silicon machines as home brew seems to place ffmpeg in different locations on the different architectures.

    At the moment I don't have a full solution for fixing either issue, however, I have written the following helper class that can check which OpenCV dependencies weren't found:

    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using System.Text;

[assembly: DefaultDllImportSearchPaths(DllImportSearchPath.LegacyBehavior)]

namespace Ansight.Studio.Mac.Services { public class OpenCvDependencyChecker : IApplicationLifecycleHandler { readonly Logging.ILogger log = Logging.Logger.Create();

    const string openCvLibaryName = "libOpenCvSharpExtern.dylib";

    public void Shutdown()
    {
    }

    public void Startup()
    {
        log?.Info("Verifying OpenCV dependencies");

        CheckFFMpegInstallation();

        log?.Info("OpenCV version is: " + OpenCvInterop.GetVersionString());
    }

    private void CheckFFMpegInstallation()
    {
        var assembly = Assembly.GetExecutingAssembly().Location;

        var assemblyFolder = Path.GetDirectoryName(assembly);
        var openCvLibaryFilePath = Path.Combine(assemblyFolder, openCvLibaryName);

        if (File.Exists(openCvLibaryFilePath))
        {
            var startInfo = new ProcessStartInfo()
            {
                FileName = "otool",
                Arguments = $"-L {openCvLibaryFilePath}",
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                RedirectStandardInput = true,
                UserName = Environment.UserName
            };
            var builder = new StringBuilder();
            using (var process = Process.Start(startInfo))
            {
                process.WaitForExit();
                builder.Append(process.StandardOutput.ReadToEnd());
            }

            var content = builder.ToString() ?? string.Empty;

            List<string> missingDependencies = new List<string>();

            if (!string.IsNullOrEmpty(content))
            {
                var dependencies = content.Split('\n', '\r');

                foreach (var line in dependencies.TakeLast(dependencies.Length - 1))
                {
                    var filePath = line.Split('(').FirstOrDefault().Trim();

                    var fileName = Path.GetFileName(filePath);

                    if (fileName == openCvLibaryName)
                    {
                        continue;
                    }

                    if (!File.Exists(filePath))
                    {
                        missingDependencies.Add(line.Trim());
                    }
                    else
                    {
                        log?.Debug("The OpenCV dependency exists: " + filePath);
                    }
                }
            }

            if (missingDependencies.Any())
            {
                log?.Warning("Unable to locate 1 or more OpenCV dependencies.");
                log?.Warning(string.Join('\n', missingDependencies));
            }
            else
            {
                log?.Info("All dependencies are resolved for OpenCV.");
            }
        }
    }
}

public enum ExceptionStatus
{

pragma warning disable 1591

    NotOccurred = 0,
    Occurred = 1
}

static class OpenCvInterop
{
    public const string DllExtern = "OpenCvSharpExtern";

    [Pure, DllImport(DllExtern, CallingConvention = CallingConvention.Cdecl, BestFitMapping = false, ThrowOnUnmappableChar = true, ExactSpelling = true)]
    public static unsafe extern ExceptionStatus core_getVersionString(byte* buf, int maxLength);

    /// <summary>
    /// Returns library version string.
    /// For example "3.4.1-dev".
    /// </summary>
    /// <returns></returns>
    public static string GetVersionString()
    {
        const int bufferSize = 128;

        unsafe
        {
            byte* buffer = stackalloc byte[bufferSize];
            core_getVersionString(buffer, bufferSize);
            var result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)buffer);
            return result;
        }
    }
}

}



 You'll need to adapt the above code for your own needs but it should get you started.