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





Существует несколько способов использования 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;
}
Ищите больше об этом. youtube.com/watch?v=pFv_1KeMZVs