OpenCV + Android + UE4.21

Hi,

I’m having a lot of trouble trying to package an Android app including OpenCV with UE4.21.
I successfully included OpenCV files for Windows 64 and I used it for an in-editor function. But when it comes to Android… ugh.

Here is the error I get when I try to package:

I found several tutos and almost no doc. But everything I got is at most for UE4.17 and not working (it is not even changing the error message a little bit).

Here is my Game.Build.cs (only the OpenCV loading function) :

    public bool LoadOpenCV (ReadOnlyTargetRules Target)
    {
        bool isLibrarySupported = false;
        string OpenCVPath = Path.Combine(ThirdPartyPath, "OpenCV");
        bool isDebug = Target.Configuration == UnrealTargetConfiguration.Debug;

        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            PublicLibraryPaths.Add(Path.Combine(OpenCVPath, "Libraries", "Win64"));
            PublicAdditionalLibraries.Add("opencv_world410.lib");
            isLibrarySupported = true;
        }
        else if (Target.Platform == UnrealTargetPlatform.Android)
        {
            bEnableExceptions = true;
            bUseRTTI = true;

            string SdkBase = Path.Combine(OpenCVPath, "Libraries", "Android", "sdk");
            string LibraryPath = Path.Combine(SdkBase, "native", "staticlibs");

            PublicIncludePaths.Add(Path.Combine(SdkBase, "native/jni/include"));
            PublicLibraryPaths.Add(Path.Combine(LibraryPath, "armeabi-v7a"));
            PublicAdditionalLibraries.Add("libopencv_calib3d.a");
            PublicAdditionalLibraries.Add("libopencv_core.a");
            PublicAdditionalLibraries.Add("libopencv_dnn.a");
            PublicAdditionalLibraries.Add("libopencv_features2d.a");
            PublicAdditionalLibraries.Add("libopencv_flann.a");
            PublicAdditionalLibraries.Add("libopencv_highgui.a");
            PublicAdditionalLibraries.Add("libopencv_imgcodecs.a");
            PublicAdditionalLibraries.Add("libopencv_imgproc.a");
            PublicAdditionalLibraries.Add("libopencv_ml.a");
            PublicAdditionalLibraries.Add("libopencv_objdetect.a");
            PublicAdditionalLibraries.Add("libopencv_photo.a");
            PublicAdditionalLibraries.Add("libopencv_stiching.a");
            PublicAdditionalLibraries.Add("libopencv_video.a");
            PublicAdditionalLibraries.Add("libopencv_videoio.a");

            PublicLibraryPaths.Add(Path.Combine(LibraryPath, "arm64-v8a"));
            PublicAdditionalLibraries.Add("libopencv_calib3d.a");
            PublicAdditionalLibraries.Add("libopencv_core.a");
            PublicAdditionalLibraries.Add("libopencv_dnn.a");
            PublicAdditionalLibraries.Add("libopencv_features2d.a");
            PublicAdditionalLibraries.Add("libopencv_flann.a");
            PublicAdditionalLibraries.Add("libopencv_highgui.a");
            PublicAdditionalLibraries.Add("libopencv_imgcodecs.a");
            PublicAdditionalLibraries.Add("libopencv_imgproc.a");
            PublicAdditionalLibraries.Add("libopencv_ml.a");
            PublicAdditionalLibraries.Add("libopencv_objdetect.a");
            PublicAdditionalLibraries.Add("libopencv_photo.a");
            PublicAdditionalLibraries.Add("libopencv_stiching.a");
            PublicAdditionalLibraries.Add("libopencv_video.a");
            PublicAdditionalLibraries.Add("libopencv_videoio.a");
            
            LibraryPath = Path.Combine(SdkBase, "native", "libs");
            RuntimeDependencies.Add(Path.Combine(LibraryPath, "armeabi-v7a/libopencv_java4.so"));
            RuntimeDependencies.Add(Path.Combine(LibraryPath, "arm64-v8a/libopencv_java4.so"));

            isLibrarySupported = true;

            AdditionalPropertiesForReceipt.Add(new ReceiptProperty("AndroidPlugin", Path.Combine(ModuleDirectory, "RocketFramesAPL.xml")));
        }
        else
        {
            string Err = string.Format("{0} dedicated server is made to depend on {1}. We want to avoid this, please correct module dependencies.", Target.Platform.ToString(), this.ToString());
            System.Console.WriteLine(Err);
        }

        if (isLibrarySupported)
        {
            PublicIncludePaths.AddRange(new string[] { Path.Combine(OpenCVPath, "Includes") });

            PublicDelayLoadDLLs.Add("opencv_world410.dll");
            PublicDelayLoadDLLs.Add("opencv__ffmpeg410_64.dll");
        }

        PublicDefinitions.Add(string.Format("WITH_OPENCV_BINDING={0}", isLibrarySupported ? 1 : 0));

        return isLibrarySupported;
    }

Here is the APL.xml file I attempted to do, but it seems to change nothing:

<?xml version="1.0" encoding="utf-8"?>
<!-- steps to add to build additions -->
<root xmlns:android="http://schemas.android.com/apk/res/android">
	<!-- init section is always evaluated once per architecture -->
	<init>
	    <setBool result="bSupported" value="false"/>
	        <isArch arch="armeabi-v7a">
	            <setBool result="bSupported" value="true"/>
	        </isArch>
          <isArch arch="arm64-v8a">
              <setBool result="bSupported" value="true"/>
          </isArch>
	</init>

	<!-- optional files or directories to copy to Intermediate/Android/APK -->
	<resourceCopies>
	    <isArch arch="armeabi-v7a">
	        <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_calib3d.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_calib3d.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_core.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_core.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_dnn.a"
                    dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_dnn.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_features2d.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_features2d.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_flann.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_flann.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_highgui.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_highgui.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_imgcodecs.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_imgcodecs.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_imgproc.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_imgproc.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_ml.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_ml.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_objdetect.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_objdetect.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_photo.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_photo.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_stiching.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_stiching.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_video.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_video.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/armeabi-v7a/libopencv_videoio.a"
	                  dst="$S(BuildDir)/staticlibs/armeabi-v7a/libopencv_videoio.a" />
          <copyFile src="$S(PluginDir)/OpenCV/libs/armeabi-v7a/libopencv_java4.so"
                    dst="$S(BuildDir)/libs/armeabi-v7a/libopencv_java4.so" />
      </isArch>
      <isArch arch="arm64-v8a">
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_calib3d.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_calib3d.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_core.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_core.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_dnn.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_dnn.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_features2d.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_features2d.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_flann.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_flann.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_highgui.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_highgui.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_imgcodecs.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_imgcodecs.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_imgproc.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_imgproc.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_ml.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_ml.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_objdetect.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_objdetect.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_photo.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_photo.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_stiching.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_stiching.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_video.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_video.a" />
          <copyFile src="$S(PluginDir)/OpenCV/staticlibs/arm64-v8a/libopencv_videoio.a"
                    dst="$S(BuildDir)/staticlibs/arm64-v8a/libopencv_videoio.a" />
          <copyFile src="$S(PluginDir)/OpenCV/libs/arm64-v8a/libopencv_java4.so"
                    dst="$S(BuildDir)/libs/arm64-v8a/libopencv_java4.so" />
      </isArch>
	</resourceCopies>

	<!-- optional libraries to load in GameActivity.java before libUE4.so -->
	<soLoadLibrary>
	    <if condition="bSupported">
	        <true>
		    <loadLibrary name="OpenCV" failmsg="Failed to load OpenCV library" />
		</true>
	    </if>
	</soLoadLibrary>
</root>

I really need to do that, it is for work and it is quite urgent :confused: I would be immensely grateful to anyone who could help me!

  • CraftyWeazel

Quick update, because I thought I was making progress: I turned my OpenCV module into an independent plugin, so that there is no issue with UE4 RTTI and all.

But still, the problem remains the same. Here is my updated .Build.cs, and the error I’m always getting :
// Copyright Rocket&AR, 2019-2029. All rights reserved.

using UnrealBuildTool;
using System.IO;

public class EexarOpenCV : ModuleRules
{
    private string ModulePath
    {
        get { return ModuleDirectory; }
    }

    private string ThirdPartyPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "../../ThirdParty/")); }
    }
    
	public EexarOpenCV(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
        
        PrivateIncludePaths.AddRange(new string[] { "EexarOpenCV/Private" });

        PublicDependencyModuleNames.AddRange(new string[] { "Core" } );
		PrivateDependencyModuleNames.AddRange(new string[] { "CoreUObject", "Engine", "Slate", "SlateCore" } );

        LoadOpenCV(Target);
    }

    public bool LoadOpenCV(ReadOnlyTargetRules Target)
    {
        bool isLibrarySupported = false;
        string OpenCVPath = Path.Combine(ThirdPartyPath, "OpenCV");
        bool isDebug = Target.Configuration == UnrealTargetConfiguration.Debug;

        if (Target.Platform == UnrealTargetPlatform.Win64)
        {
            System.Console.WriteLine("Loading OpenCV for Windows 64");

            isLibrarySupported = true;

            PublicLibraryPaths.Add(Path.Combine(OpenCVPath, "Libraries", "Win64"));
            PublicAdditionalLibraries.Add("opencv_world410.lib");

            string DllPathOne = Path.Combine(OpenCVPath, "Libraries/Win64", "opencv_ffmpeg410_64.dll");
            string DllPathTwo = Path.Combine(OpenCVPath, "Libraries/Win64", "opencv_world410.dll");
            RuntimeDependencies.Add(DllPathOne);
            RuntimeDependencies.Add(DllPathTwo);

            string BinariesPath = Path.Combine(ModulePath, "../../Binaries/Win64");
            if (!Directory.Exists(BinariesPath))
                System.IO.Directory.CreateDirectory(BinariesPath);

            CopyFile(DllPathOne, Path.Combine(BinariesPath, "opencv_ffmpeg410_64.dll"));
            CopyFile(DllPathTwo, Path.Combine(BinariesPath, "opencv_world410.dll"));

            PublicDelayLoadDLLs.AddRange(new string[] { "opencv_ffmpeg410_64.dll", "opencv_world410.dll" });
        }
        else if (Target.Platform == UnrealTargetPlatform.Android)
        {
            System.Console.WriteLine("Loading OpenCV for Android");

            bEnableExceptions = true;
            bUseRTTI = true;
            isLibrarySupported = true;

            string SdkBase = Path.Combine(OpenCVPath, "Libraries", "Android", "sdk");
            
            //Shared
            string LibraryPath = Path.Combine(SdkBase, "native", "libs"); PublicLibraryPaths.Add(Path.Combine(LibraryPath, "armeabi-v7a"));
            PublicLibraryPaths.Add(Path.Combine(LibraryPath, "arm64-v8a"));
            
            PublicAdditionalLibraries.Add("opencv_java4");

            //RuntimeDependencies.Add(Path.Combine(LibraryPath, "armeabi-v7a/libopencv_java4.so"));
            //RuntimeDependencies.Add(Path.Combine(LibraryPath, "arm64-v8a/libopencv_java4.so"));

            /*//Static - I AM NOT USING .a RIGHT NOW. SHOULD I?
            LibraryPath = Path.Combine(SdkBase, "native", "staticlibs");
            PublicLibraryPaths.Add(Path.Combine(LibraryPath, "armeabi-v7a"));
            PublicLibraryPaths.Add(Path.Combine(LibraryPath, "arm64-v8a"));

            PublicAdditionalLibraries.Add("opencv_calib3d");
            PublicAdditionalLibraries.Add("opencv_core");
            PublicAdditionalLibraries.Add("opencv_dnn");
            PublicAdditionalLibraries.Add("opencv_features2d");
            PublicAdditionalLibraries.Add("opencv_flann");
            PublicAdditionalLibraries.Add("opencv_highgui");
            PublicAdditionalLibraries.Add("opencv_imgcodecs");
            PublicAdditionalLibraries.Add("opencv_imgproc");
            PublicAdditionalLibraries.Add("opencv_ml");
            PublicAdditionalLibraries.Add("opencv_objdetect");
            PublicAdditionalLibraries.Add("opencv_photo");
            PublicAdditionalLibraries.Add("opencv_stitching");
            PublicAdditionalLibraries.Add("opencv_video");
            PublicAdditionalLibraries.Add("opencv_videoio");*/

            //APL
            string RelAPLPath = Path.Combine(ModuleDirectory, @"..\..\ThirdParty\OpenCV\OpenCV_APL.xml");
            AdditionalPropertiesForReceipt.Add("AndroidPlugin", RelAPLPath);
        }

        if (isLibrarySupported)
        {
            PrivateIncludePaths.AddRange(new string[] { Path.Combine(OpenCVPath, "Includes") });
        }

        PublicDefinitions.Add(string.Format("WITH_OPENCV_BINDING={0}", isLibrarySupported ? 1 : 0));

        return isLibrarySupported;
    }

    private void CopyFile(string source, string dest)
    {
        System.Console.WriteLine("Copying {0} to {1}", source, dest);
        if (System.IO.File.Exists(dest))
        {
            System.IO.File.SetAttributes(dest, System.IO.File.GetAttributes(dest) & ~System.IO.FileAttributes.ReadOnly);
        }
        try
        {
            System.IO.File.Copy(source, dest, true);
        }
        catch (System.Exception ex)
        {
            System.Console.WriteLine("Failed to copy file: {0}", ex.Message);
        }
    }
}

The error I’m getting is coming from the first (and only) usage of OpenCV i have in my code, even if it completely works on Windows.

 ------ Début de la génération : Projet : RocketFrames, Configuration : Android_Shipping Win32 ------
    Using 'git status' to determine working set for adaptive non-unity build (D:\Thomas\Documents\GitHub\UnrealEngine).
    Using 'git status' to determine working set for adaptive non-unity build (D:\Thomas\Documents\Unreal Projects\RocketFrames).
    Creating makefile for RocketFrames (source directory changed)
    PLATFORM_ANDROID_NDK_VERSION = 140200
    NDK toolchain: r14b, NDK version: 24, GccVersion: 4.9, ClangVersion: 3.8.275480
    Loading OpenCV for Android
    Compiling Native code with NDK API 'android-24'
    Building 2 actions with 4 processes...
      [1/2] EexarOpenCV.cpp [armv7-es2]
      [2/2] RocketFrames-Android-Shipping-armv7-es2.so
      D:/Thomas/Documents/Unreal Projects/RocketFrames/Plugins/EexarOpenCV/Source/EexarOpenCV/Private/EexarOpenCV.cpp:40: error: undefined reference to 'cv::imread(std::string const&, int)'
    clang++.exe : error : linker command failed with exit code 1 (use -v to see invocation)
    UnrealBuildTool : error : UBT ERROR: Failed to produce item: D:\Thomas\Documents\Unreal Projects\RocketFrames\Binaries\Android\RocketFrames-Android-Shipping-armv7-es2.so
                            (see ../Programs/UnrealBuildTool/Log.txt for full exception trace)
    Total build time: 252.27 seconds (Parallel executor: 0.00 seconds)
    C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\VC\VCTargets\Microsoft.MakeFile.Targets(44,5): error MSB3075: La commande "D:\Thomas\Documents\GitHub\UnrealEngine\Engine\Build\BatchFiles\Build.bat RocketFrames Android Shipping "D:\Thomas\Documents\Unreal Projects\RocketFrames\RocketFrames.uproject" -WaitMutex -FromMsBuild" s'est arrêtée avec le code 5. Assurez-vous d'avoir les droits suffisants pour exécuter cette commande.
    Génération du projet "RocketFrames.vcxproj" terminée -- ÉCHEC.
    ========== Génération : 0 a réussi, 1 a échoué, 0 mis à jour, 0 a été ignoré ==========

My includes in the incriminated .cpp file looks like this:

THIRD_PARTY_INCLUDES_START
#include "UndefineUnrealMacros.h"
#include "opencv2/opencv.hpp"
#include "opencv2/imgcodecs.hpp"
#include "RedefineUnrealMacros.h"
THIRD_PARTY_INCLUDES_END

This “UndefineUnrealMacros.h” and “RedefineUnrealMacros.h” are made to deactivate all conflicting macros between UE and OpenCV.

Any help ? This is getting so urgent at work, I’m in trouble :confused: