如何将Xamarin.Android与OpenCV集成

5

我正在尝试在Visual Studio中连接Xamarin.android和OpenCV,但文档非常简陋,有人能提供一些步骤来帮助我吗?


搜索更多相关信息。https://www.youtube.com/watch?v=pFv_1KeMZVs - Marfin. F
1个回答

12

在Xamarin.Android上使用OpenCV有多种方法:

1 - 使用OpenCV4Android的Binding:OpenCV4Android是一个使用JNI将OpenCV(C++)封装为Android(Java)的包装器。通过这个绑定,我们可以在Java和C#之间建立接口(更多信息请参见https://learn.microsoft.com/en-us/xamarin/android/platform/binding-java-library/)。

它被实现在https://github.com/jeremy-ellis-tech/Xamarin.Android.OpenCV中,其中使用了OpenCV 3.1.0。 您可以按照安装说明和“减小.dll文件大小”的步骤生成一个dll并引用它,或者将“/src/OpenCV.Binding”文件夹中的Visual Studio项目插入到您的Visual Studio解决方案中,并将您的项目添加到这个新项目的引用中。

如果您想使用更新的版本,可以从OpenCV SourceForge下载更新的OpenCV4Android版本(名称为opencv-version-android-sdk.zip的文件,例如链接上的OpenCV4Android 4.1.0:https://sourceforge.net/projects/opencvlibrary/files/4.1.0/),并用先前项目中“/src/OpenCV.Binding/Jars”文件夹的内容替换为提取的OpenCV4Android的“/sdk/native/libs”和“/sdk/native/3rdparty/libs”文件夹的内容。

还有一个这个项目的分支的NuGet:https://www.nuget.org/packages/Xamarin.OpenCV.Droid,可以简化安装和使用,但我没有使用过,所以不能确定是否有效。

由于这种方法是OpenCV4Android的Binding而不是纯OpenCV,因此您将使用OpenCV4Android的文档(https://opencv.org/android/)。同时值得一提的是,这种方式有三层编程语言(C# - Java - C++),因此在调用方法时会有性能损失(JNI是一个负担)。因此,建议您尽可能少地进行调用。

2 - 使用OpenCV C++的Wrapper:通过这种方式,我们将使用C++共享库(.so)并从C#中调用其方法(https://learn.microsoft.com/en-us/xamarin/android/platform/native-libraries)。 为了实现这个目标,我们需要编写OpenCV方法的PInvoke,这是很多工作,意味着需要花费很多时间。因此,我们将使用其他人已经编写好的内容。

我们有OpenCvSharp,它是OpenCV到.NET的包装器,显然运行良好。问题是:它不兼容ARM,因此无法在智能手机上运行。 然而,一个好心人将其适配为可在ARM设备上运行:https://github.com/Kawaian/OpenCvSharp
如何快速使用它:将文件夹“/src/OpenCvSharp”中的项目插入您的解决方案并进行引用。将“/src/OpenCvSharp.Android/Native”文件夹中的内容复制到您的项目的“lib”或“libs”文件夹中。然后将“.so”文件配置为“始终复制”到输出目录,并将其构建操作配置为“AndroidNativeLibrary”(如果您的项目是应用程序)或“Embedded Native Library”(如果您的项目是Android库)。
另一种方法是安装NuGet (https://www.nuget.org/packages/Kawaian.OpenCVSharp/),这使得操作稍微简单一些,但也需要将“.so”文件复制到“lib”或“libs”并进行配置。
此包装器使用OpenCV 3.2.0。我正在研究一种更新该项目中OpenCV版本的方法,但目前仍在运行。
这种方法的巨大优势是性能(与两种实现相比,我的应用程序性能提高了约30%)。但是它的一个缺点是没有已制作的Android.Bitmap - OpenCV.Mat转换方法。我通过调整OpenCV4Android的转换方法来实现它们。
// Method adapted from the original OpenCV convert at https://github.com/opencv/opencv/blob/b39cd06249213220e802bb64260727711d9fc98c/modules/java/generator/src/cpp/utils.cpp
///<summary>
///This function converts an image in the OpenCV Mat representation to the Android Bitmap.
///The input Mat object has to be of the types 'CV_8UC1' (gray-scale), 'CV_8UC3' (RGB) or 'CV_8UC4' (RGBA).
///The output Bitmap object has to be of the same size as the input Mat and of the types 'ARGB_8888' or 'RGB_565'.
///This function throws an exception if the conversion fails.
///</summary>
///<param name="srcImage">srcImage is a valid input Mat object of types 'CV_8UC1', 'CV_8UC3' or 'CV_8UC4'</param>
///<param name="dstImage">dstImage is a valid Bitmap object of the same size as the Mat and of type 'ARGB_8888' or 'RGB_565'</param>
///<param name="needPremultiplyAlpha">premultiplyAlpha is a flag, that determines, whether the Mat needs to be converted to alpha premultiplied format (like Android keeps 'ARGB_8888' bitmaps); the flag is ignored for 'RGB_565' bitmaps.</param>
public static void MatToBitmap(Mat srcImage, Bitmap dstImage, bool needPremultiplyAlpha = false)
{
    var bitmapInfo = dstImage.GetBitmapInfo();
    var bitmapPixels = dstImage.LockPixels();
 
    if (bitmapInfo.Format != Format.Rgba8888 && bitmapInfo.Format != Format.Rgb565)
        throw new Exception("Invalid Bitmap Format: It is of format " + bitmapInfo.Format.ToString() + " and is expected Rgba8888 or Rgb565");
    if (srcImage.Dims() != 2)
        throw new Exception("The source image has " + srcImage.Dims() + " dimensions, while it is expected 2");
    if (srcImage.Cols != bitmapInfo.Width || srcImage.Rows != bitmapInfo.Height)
        throw new Exception("The source image and the output Bitmap don't have the same amount of rows and columns");
    if (srcImage.Type() != MatType.CV_8UC1 && srcImage.Type() != MatType.CV_8UC3 && srcImage.Type() != MatType.CV_8UC4)
        throw new Exception("The source image has the type " + srcImage.Type().ToString() + ", while it is expected CV_8UC1, CV_8UC3 or CV_8UC4");
    if (bitmapPixels == null)
        throw new Exception("Can't lock the output bitmap");
 
    if (bitmapInfo.Format == Format.Rgba8888)
    {
        Mat tmp = new Mat((int)bitmapInfo.Height, (int)bitmapInfo.Width, MatType.CV_8UC4, bitmapPixels);
        if (srcImage.Type() == MatType.CV_8UC1)
        {
            Android.Util.Log.Info("MatToBitmap", "CV_8UC1 -> RGBA_8888");
            Cv2.CvtColor(srcImage, tmp, ColorConversionCodes.GRAY2RGBA);
        }
        else if (srcImage.Type() == MatType.CV_8UC3)
        {
            Android.Util.Log.Info("MatToBitmap", "CV_8UC3 -> RGBA_8888");
            Cv2.CvtColor(srcImage, tmp, ColorConversionCodes.RGB2RGBA);
        }
        else if (srcImage.Type() == MatType.CV_8UC4)
        {
            Android.Util.Log.Info("MatToBitmap", "CV_8UC4 -> RGBA_8888");
            if (needPremultiplyAlpha)
                Cv2.CvtColor(srcImage, tmp, ColorConversionCodes.RGBA2mRGBA);
            else
                srcImage.CopyTo(tmp);
        }
    }
    else
    {
        // info.format == ANDROID_BITMAP_FORMAT_RGB_565
        Mat tmp = new Mat((int)bitmapInfo.Height, (int)bitmapInfo.Width, MatType.CV_8UC2, bitmapPixels);
        if (srcImage.Type() == MatType.CV_8UC1)
        {
            Android.Util.Log.Info("MatToBitmap", "CV_8UC1 -> RGB_565");
            Cv2.CvtColor(srcImage, tmp, ColorConversionCodes.GRAY2BGR565);
        }
        else if (srcImage.Type() == MatType.CV_8UC3)
        {
            Android.Util.Log.Info("MatToBitmap", "CV_8UC3 -> RGB_565");
            Cv2.CvtColor(srcImage, tmp, ColorConversionCodes.RGB2BGR565);
        }
        else if (srcImage.Type() == MatType.CV_8UC4)
        {
            Android.Util.Log.Info("MatToBitmap", "CV_8UC4 -> RGB_565");
            Cv2.CvtColor(srcImage, tmp, ColorConversionCodes.RGBA2BGR565);
        }
    }
    dstImage.UnlockPixels();
    return;
}

// Method adapted from the original OpenCV convert at https://github.com/opencv/opencv/blob/b39cd06249213220e802bb64260727711d9fc98c/modules/java/generator/src/cpp/utils.cpp
///<summary>
///This function converts an Android Bitmap image to the OpenCV Mat.
///'ARGB_8888' and 'RGB_565' input Bitmap formats are supported.
///The output Mat is always created of the same size as the input Bitmap and of the 'CV_8UC4' type,it keeps the image in RGBA format.
///This function throws an exception if the conversion fails.
///</summary>
///<param name="srcImage">srcImage is a valid input Bitmap object of the type 'ARGB_8888' or 'RGB_565'</param>
///<param name="dstImage">dstImage is a valid output Mat object, it will be reallocated if needed, so it may be empty.</param>
///<param name="needUnPremultiplyAlpha">unPremultiplyAlpha is a flag, that determines, whether the bitmap needs to be converted from alpha premultiplied format (like Android keeps 'ARGB_8888' ones) to regular one; this flag is ignored for 'RGB_565' bitmaps.</param>
public static void BitmapToMat(Bitmap srcImage, Mat dstImage, bool needUnPremultiplyAlpha = false)
{
    var bitmapInfo = srcImage.GetBitmapInfo();
    var bitmapPixels = srcImage.LockPixels();
 
    if (bitmapInfo.Format != Format.Rgba8888 && bitmapInfo.Format != Format.Rgb565)
        throw new Exception("Invalid Bitmap Format: It is of format " + bitmapInfo.Format.ToString() + " and is expected Rgba8888 or Rgb565");
    if (bitmapPixels == null)
        throw new Exception("Can't lock the source bitmap");
 
    dstImage.Create((int)bitmapInfo.Height, (int)bitmapInfo.Width, MatType.CV_8UC4);
 
    if (bitmapInfo.Format == Format.Rgba8888)
    {
        Android.Util.Log.Info("nBitmapToMat", "RGBA_8888 -> CV_8UC4");
        Mat tmp = new Mat((int)bitmapInfo.Height, (int)bitmapInfo.Width, MatType.CV_8UC4, bitmapPixels);
        if (needUnPremultiplyAlpha)
            Cv2.CvtColor(tmp, dstImage, ColorConversionCodes.mRGBA2RGBA);
        else
            tmp.CopyTo(dstImage);
    }
    else
    {
        // info.format == ANDROID_BITMAP_FORMAT_RGB_565
        Android.Util.Log.Info("nBitmapToMat", "RGB_565 -> CV_8UC4");
        Mat tmp = new Mat((int)bitmapInfo.Height, (int)bitmapInfo.Width, MatType.CV_8UC2, bitmapPixels);
        Cv2.CvtColor(tmp, dstImage, ColorConversionCodes.BGR5652RGBA);
    }
 
    srcImage.UnlockPixels();
    return;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接