takuya-takeuchi / FaceRecognitionDotNet

The world's simplest facial recognition api for .NET on Windows, MacOS and Linux
MIT License
1.24k stars 304 forks source link

Update nuget package to support "anyCPU" platform #114

Open programatix opened 4 years ago

programatix commented 4 years ago

Hi,

I'm using the nuget package and if I configure my project to "any" platform, it will fail to build with the error message,

6>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(4643,5): error MSB3030: Could not copy the file "C:\Users\...\.nuget\packages\dlibdotnet\19.18.0.20200525\runtimes\win-AnyCPU\native\DlibDotNetNativeDnn.dll" because it was not found.
6>C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\MSBuild\Current\Bin\Microsoft.Common.CurrentVersion.targets(4643,5): error MSB3030: Could not copy the file "C:\Users\...\.nuget\packages\dlibdotnet\19.18.0.20200525\runtimes\win-AnyCPU\native\DlibDotNetNative.dll" because it was not found.

Is it possible to update the deployment script to include "any" which include both 32-bit and 64-bit runtimes?

takuya-takeuchi commented 4 years ago

@programatix FRDN depends on native binary and we take of architecture of binary. https://github.com/takuya-takeuchi/FaceRecognitionDotNet/wiki/Quickstart#windows

Is it possible to update the deployment script to include "any" which include both 32-bit and 64-bit runtimes? I have no idea.

CCE-DV commented 3 years ago

If you need to build your app as AnyCpu (win-AnyCPU), eg user control issues when building as x64 and drag and drop in x32 IDE issues etc..

Create Folder win-AnyCPU like this: %USERPROFILE%.nuget\packages\DlibDotNet.19.21.0.20210228\runtimes\win-AnyCPU

Then copy win-x64into win-AnyCPU folder- > builds then work as AnyCPU

%USERPROFILE%.nuget\packages\DlibDotNet.19.21.0.20210228\runtimes\win-x64

Note: Use Nuget Package Manager Console - Solution to reinstall current nuget packages if you switch DotNet Frameworks

Nuget Package Manager Console command to refresh nugets (reinstall for current framework) Update-Package –reinstall

CCE-DV commented 3 years ago

This is a batch file to assist with your missing AnyCPU

IF EXIST "C:\Users\%username%\.nuget\packages\dlibdotnet\19.21.0.20210228\runtimes\win-AnyCPU\" ( 
    ECHO folder anyCPU already exists
) ELSE ( 
ECHO AnyCPU Foldlder does not exist, copying x64 to anyCPU
 xcopy "C:\Users\%username%\.nuget\packages\dlibdotnet\19.21.0.20210228\runtimes\win-x64\"  "C:\Users\%username%\.nuget\packages\dlibdotnet\19.21.0.20210228\runtimes\win-AnyCPU\" /E /I /Y )
programatix commented 3 years ago

I haven't tried this, but wouldn't this use the x64 native and it would have caused runtime error when running on a x86 machine, no?

I was thinking of having both the native binary of x86 and x64 referred in the FaceRecognitionDotNet DLL where when it refers to the native binary, it would choose which version to call during runtime.

Something like this,

#if __BUILD_BOTH__
#define __BUILD_32_BIT__
#define __BUILD_64_BIT__
#endif

//...
//...
    {
        static bool Is64bit = (IntPtr.Size == 8);

#if __BUILD_32_BIT__
        [DllImport("NativeDll.x86.dll")]
        static extern bool NativeFunction32(string in1, string in2);
#endif

#if __BUILD_64_BIT__
        [DllImport("NativeDll.x64.dll")]
        static extern bool NativeFunction64(string in1, string in2);
#endif

        static bool NativeFunction(string in1, string in2)
        {
            if (Is64bit)
#if __BUILD_64_BIT__
                return NativeFunction64(in1, in2);
#else
                throw new Exception("Missing 64 bit native DLLs");
#endif
            else
#if __BUILD_32_BIT__
                return NativeFunction32(in1, in2);
#else
                throw new Exception("Missing 32 bit native DLLs");
#endif
        }
    }

This way, FaceRecognitionDotNet can have 3 builds,

  1. 32-bits only
  2. 64-bits only
  3. Both (any)
CCE-DV commented 3 years ago

Hi programatix

There is a simple project demonstrating how to reference managed C++ functionality from a C# AnyCPU dll

Managed.AnyCPU https://github.com/kevin-marshall/Managed.AnyCPU

I believe the cleanest way to reference managed C++ functionality from a C# dll, compiled with the AnyCPU setting, is to create a C# front end to the C++ functionality.

In order for the C++ functionality to be consumed by a C# dll, the C++ project must produce both x86 and x64 versions of the dll. It is impossible to reference just a x86 or a x64 dll from a C# dll compiled with the AnyCPU setting.

programatix commented 2 years ago

Not trying to complain but this is getting more and more difficult to handle. Locally, there any many workarounds, but when used with Cloud Platforms like Azure DevOps, builds are failing when Target Platform is AnyCPU. AnyCPU is usually used because the build may be used on a x86 or x64 with a single build.

Perhaps we could refer to other projects such as https://github.com/dlemstra/Magick.NET which has similar dependency on native binaries. The AnyCPU release has both the x64 and x86 binaries.

programatix commented 2 years ago

I encountered HTTP Error 500.30 - ASP.NET Core app failed to start issue when publishing Web App (Windows) in Azure. Turns out that the issue is due to the project build built as "x64". It seems to only be able to run when configured to "anyCPU".

At the server, this is the error when started from the command line,

Unhandled exception. System.BadImageFormatException: Could not load file or assembly 'C:\home\site\wwwroot\MyWebApp.dll'. An attempt was made to load a program with an incorrect format.

Update: Seems like the "platform" depends on the App Server Plan chosen. In my test environment case, turns out that it is "x86".

takuya-takeuchi commented 2 years ago

I have not checked Magck.NET code yet.

image

But nuget package has not special trick execept for *.targets

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <NativeDLL Include="$(MSBuildThisFileDirectory)\..\..\runtimes\**\*.dll" />
    <Content Include="@(NativeDLL)">
      <Link>%(FileName)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</Project>
programatix commented 2 years ago

Hi,

I noted 2 methods of "placing" the DLLs. In Magick.NET case, the DLLs are copied into the "runtimes" directory (using the nuget package directory structure). It doesn't show in Visual Studio Solution Explorer though.

image

In OpenCvSharp4 case, (it's OpenCvSharp4.runtime.xxx), it copied the DLLs in the following directory structure. Similar to the method you're using, the files are shown in Visual Studio Solution Explorer.

image

I think using nuget directory structure is better since I'm seeing many other packages are using this structure. On accessing the DLLs, I believe they should be using something as I mentioned above in https://github.com/takuya-takeuchi/FaceRecognitionDotNet/issues/114#issuecomment-781282012 where a wrapper function is used to call the native function in the correct DLL.

takuya-takeuchi commented 2 years ago

I know about opencv4 trick but it make package fat file. Especially, dlibdotnet.native.dnn is too large. To be honest, I think we should not take care of both x86 and x64. OSX and Linux has already abandoned x86. Face recognition modlel files consume not few memory so x86 can not put up with it.

programatix commented 2 years ago

Well, it would only make the AnyCPU package fat. Compared to other nuget packages, I think the files DlibDotNetNativeDnnAgeClassification.dll and DlibDotNetNativeDnnGenderClassification.dll in facerecognitiondotnet are not big, no?

Also, the combination on DlibDotNetNative.dll and DlibDotNetNativeDnn.dll are about the size of a single Magick.NET native DLL.

For my case, I still need to use x86 because some devices I'm using only provide the x86 DLLs, such as the iris scanners and fingerprint scanners. Additionally AnyCPU apps are easier to deploy since it can be deployed on both x86 and x64 machines.

takuya-takeuchi commented 2 years ago

Oh, I forgot a acutual size of DlibDotNet. I talked about pacakge for android. And DlibDotNet (cpu binary) has already x86 and x64 binaries. So it may be easy to support any cpu.

BTW, Magick.NET trick is available in .NET framework?

programatix commented 2 years ago

Magick.NET nuget packages are

So it covered both .NET Framework and .NET Core. I just realized that if you go this route, you'll have to publish at least 2 nuget packages.