Как интегрировать Xamarin.android с OpenCV

Я пытаюсь подключить Xamarin.android к OpenCV в Visual Studio, документация действительно плохая, может ли кто-нибудь дать мне несколько шагов, как это сделать?

Ищите больше об этом. youtube.com/watch?v=pFv_1KeMZVs

Marfin. F 12.05.2019 05:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
1
2 927
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Существует несколько способов использования OpenCV на Xamarin.Android:

1 — использование привязки OpenCV4Android: OpenCV4Android — это оболочка OpenCV (C++) для Android (Java) с использованием JNI. С привязкой у нас есть интерфейс между Java и C# (дополнительная информация в https://docs.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 и сослаться на нее, или вы можете вставить в свое решение Visual Studio проект Visual Studio из папки «/src/OpenCV.Binding» и добавить ссылку из ваш проект в этот новый проект.

Если вы хотите использовать более новую версию, вы можете загрузить более новую версию OpenCV4Android (файл с именем opencv-version-android-sdk.zip в OpenCV SourceForge, например: OpenCV4Android 4.1.0 по ссылке https://sourceforge.net/projects/opencvlibrary/files/4.1.0/) и заменить содержимое папки «/src/OpenCV.Binding/Jars» в предыдущем проекте с содержимым папок «/sdk/native/libs» и «/sdk/native/3rdparty/libs» извлеченного файла OpenCV4Android.

Также есть NuGet форка этого проекта: https://www.nuget.org/packages/Xamarin.OpenCV.Droid, который может упростить установку и использование, но я его не использовал, поэтому не могу сказать, работает ли он.

Поскольку этот метод является привязкой OpenCV4Android, а не чистым OpenCV, вы будете использовать документацию OpenCV4Android (https://opencv.org/android/). Также стоит сказать, что таким образом у нас есть три слоя языков программирования (C# - Java - C++), поэтому у нас есть потери производительности при вызове методов (JNI - это бремя). Поэтому рекомендуется использовать как можно меньше вызовов.

2 — использование оболочки OpenCV C++: таким образом мы будем использовать общие библиотеки C++ (.so) и вызывать их методы из C# (https://docs.microsoft.com/en-us/xamarin/android/platform/native-libraries). Для этого нам нужно было бы написать PInvoke методов OpenCV, которых много, а значит много времени. Так что будем использовать то, что кто-то уже сделал.

У нас есть OpenCvSharp, который является оболочкой OpenCV для .NET и, по-видимому, работает хорошо. Проблема: он не совместим с ARM, поэтому не будет работать на смартфонах. Однако добрая душа адаптировала его для работы на устройствах ARM: https://github.com/Kawaian/OpenCvSharp.

Как это использовать быстро: вы вставляете проект папки «/src/OpenCvSharp» в свое решение и ссылаетесь на него. Вы копируете содержимое «/src/OpenCvSharp.Android/Native» в папку «lib» или «libs» вашего проекта. Затем вы настраиваете файлы «.so» на «Всегда копировать» в каталог OutPut и настраиваете их действие сборки на «AndroidNativeLibrary» (если ваш проект является приложением) или «Встроенная нативная библиотека» (если ваш проект — библиотека 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;
}

Другие вопросы по теме