GooglePlayServicesComponents icon indicating copy to clipboard operation
GooglePlayServicesComponents copied to clipboard

Xamarin.Google.MLKit.Vision.Objects.Defaults.ObjectDetectorOptions.Builder has no required public methods from original Java class

Open mmarinchenko opened this issue 4 years ago • 16 comments

Xamarin.Android Version:

11.1.0.26 (Visual Studio Community 2019 v16.8.5)

Operating System & Version:

Windows 10 Home Single Language 20H2 v19042.804

Google Play Services Version

117.6.0 with AndroidX dependencies

Relevant information

Packages used:

    <Reference Include="Mono.Android" />
    <Reference Include="System" />
    <PackageReference Include="Xamarin.Google.Android.Material">
      <Version>1.3.0</Version>
    </PackageReference>
    <PackageReference Include="Xamarin.Google.MLKit.ObjectDetection">
      <Version>116.2.0</Version>
    </PackageReference>

Google ML Kit Object Detection Guide with Kotlin and Java samples:

  • https://developers.google.com/ml-kit/vision/object-detection/android

Related Google ML Kit docs:

  • https://developers.google.cn/android/reference/com/google/mlkit/vision/objects/ObjectDetectorOptionsBase
  • https://developers.google.cn/android/reference/com/google/mlkit/vision/objects/defaults/ObjectDetectorOptions
  • https://developers.google.cn/android/reference/com/google/mlkit/vision/objects/defaults/ObjectDetectorOptions.Builder

Minimal Repro Code Sample

Get the following Java code from Google ML Kit Object Detection Guide (link provided above):

// Multiple object detection in static images
ObjectDetectorOptions options =
        new ObjectDetectorOptions.Builder()
                .setDetectorMode(ObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableMultipleObjects()
                .enableClassification()  // Optional
                .build();

and write it in C#:

using Xamarin.Google.MLKit.Vision.Objects.Defaults;
<...>
// Multiple object detection in static images
var options = new ObjectDetectorOptions.Builder()
        .SetDetectorMode(ObjectDetectorOptions.SingleImageMode)
        .EnableMultipleObjects()
        .EnableClassification()  // Optional
        .Build();

This will not compile because all Builder's methods are missing, except Build().

mmarinchenko avatar Feb 22 '21 11:02 mmarinchenko

I tried to overcome this by implementing a subclass of ObjectDetectorOptionsBase which has its own nested protected internal abstract class Builder with required methods.

Result looks odd... I had to implement 2 nested classes for that:

  • one as private implementation of protected abstract Builder which required for ObjectDetectorOptionsBase constructor
  • and another one as public wrapper to expose API to consumers of new subclass
DetectorOptions.cs (click to expand)
    public sealed class DetectorOptions : ObjectDetectorOptionsBase  // Java.Lang.Object type
    {
        private DetectorOptions(BaseBuilder baseBuilder) : base(baseBuilder) { }

        private sealed class BaseBuilder : ObjectDetectorOptionsBase.Builder  // Java.Lang.Object type
        {
            private readonly Builder _builder;

            public BaseBuilder(Builder builder) => _builder = builder;

            protected override ObjectDetectorOptionsBase Build() => _builder.Build();
        }

        new public sealed class Builder  // pure managed type, hides base.Builder
        {
            private readonly BaseBuilder _baseBuilder;

            private readonly DetectorOptions _detectorOptions;

            public Builder()
            {
                _baseBuilder = new BaseBuilder(this);
                _detectorOptions = new DetectorOptions(_baseBuilder);
            }

            public Builder SetDetectorMode(int detectorMode)
            {
                _baseBuilder.SetDetectorMode(detectorMode);
                return this;
            }

            public Builder SetExecutor(IExecutor executor)
            {
                _baseBuilder.SetExecutor(executor);
                return this;
            }

            public Builder EnableClassification()
            {
                _baseBuilder.EnableClassification();
                return this;
            }

            public Builder EnableMultipleObjects()
            {
                _baseBuilder.EnableMultipleObjects();
                return this;
            }

            public DetectorOptions Build() => _detectorOptions;
        }
    }

So now I can write this in C# and it compiles fine:

var options = new DetectorOptions.Builder()
        .SetDetectorMode(ObjectDetectorOptionsBase.SingleImageMode)
        .EnableMultipleObjects()
        .EnableClassification()  // Optional
        .Build();

But unfortunately this doesn't compile in Java due to DetectorOptions.BaseBuilder is translated into a separate public class which does not have access to the nested protected ObjectDetectorOptionsBase.Builder:

Output (click to expand)
1>  2 errors
1>...\DetectorOptions_BaseBuilder.java(5,67): javac.exe error JAVAC0000:  error: Builder has protected access in ObjectDetectorOptionsBase
1>...\DetectorOptions_BaseBuilder.java(5,67): javac.exe error JAVAC0000: 	extends com.google.mlkit.vision.objects.ObjectDetectorOptionsBase.Builder
1>...\DetectorOptions_BaseBuilder.java(5,67): javac.exe error JAVAC0000: 
1>...\DetectorOptions_BaseBuilder.java(22,7): javac.exe error JAVAC0000:  error: cannot find symbol
1>...\DetectorOptions_BaseBuilder.java(22,7): javac.exe error JAVAC0000: 		if (getClass () == DetectorOptions_BaseBuilder.class)
1>...\DetectorOptions_BaseBuilder.java(22,7): javac.exe error JAVAC0000:   symbol:   method getClass()
1>...\DetectorOptions_BaseBuilder.java(22,7): javac.exe error JAVAC0000:   location: class DetectorOptions_BaseBuilder
DetectorOptions_BaseBuilder.java (click to expand)
package crc641d15e8c705d2fdbc;


public class DetectorOptions_BaseBuilder
	extends com.google.mlkit.vision.objects.ObjectDetectorOptionsBase.Builder  // first compilation error
	implements
		mono.android.IGCUserPeer
{
/** @hide */
	public static final String __md_methods;
	static {
		__md_methods = 
			"n_build:()Lcom/google/mlkit/vision/objects/ObjectDetectorOptionsBase;:GetBuildHandler\n" +
			"";
		mono.android.Runtime.register ("MSSLink.CameraApp.Detection.DetectorOptions+BaseBuilder, MSSLink.CameraApp", DetectorOptions_BaseBuilder.class, __md_methods);
	}


	public DetectorOptions_BaseBuilder ()
	{
		super ();
		if (getClass () == DetectorOptions_BaseBuilder.class)  // second compilation error
			mono.android.TypeManager.Activate ("MSSLink.CameraApp.Detection.DetectorOptions+BaseBuilder, MSSLink.CameraApp", "", this, new java.lang.Object[] {  });
	}


	public com.google.mlkit.vision.objects.ObjectDetectorOptionsBase build ()
	{
		return n_build ();
	}

	private native com.google.mlkit.vision.objects.ObjectDetectorOptionsBase n_build ();

	private java.util.ArrayList refList;
	public void monodroidAddReference (java.lang.Object obj)
	{
		if (refList == null)
			refList = new java.util.ArrayList ();
		refList.add (obj);
	}

	public void monodroidClearReferences ()
	{
		if (refList != null)
			refList.clear ();
	}
}

Is there a way to translate a nested Java.Lang.Object type written in C# to a nested class in Java while preserving the access modifiers?

mmarinchenko avatar Feb 22 '21 15:02 mmarinchenko

@mmarinchenko Hi, did you find a solution for this?

AlexDevMobile avatar Feb 24 '21 20:02 AlexDevMobile

@AlerzDev Hi! I have 2 issues here:

  • missing methods in some Builder classes (original one)
  • generating a separate public ACW for a nested managed class which inherits a protected nested java class (and therefore could not be un-nested from descendant) - see my comment above.

I'm not sure it's right to discuss second issue here. Maybe it should be moved to Xamarin.Android repo. Anyway I don't have a solution for this.


As for original issue I assume the following bindings are incomplete:

Each Transforms/Metadata.xml file in these directories has custom binding rules and the following (or similar) comment:

    <!--
    TODO: possible problems! return type covariance/contravariance (needs investigation)
    
    removing `build` with return="<...>"
    renaming in Addditions
    -->

but only one of corresponding Additions/***Options.cs files (the image-labeling-automl one) actually implements some logic.

So I'm waiting for a comment from @moljac. Or from someone else who can bring this to light :)

mmarinchenko avatar Feb 26 '21 08:02 mmarinchenko

@AlerzDev

Thanks for the feedack

missing methods in some Builder classes (original one)

That happens for various reasons (difference between java and c# idioms, generics implementations,...). Even our tooling and our/my mistakes.

I rushed to publish MLKit and to catch up with google, so I might have made some mistakes.

Furthermore - the quality of bindings cannot be determined w/o samples or API testing (unit test). Are you getting where am I heading? The more minimal sample you guys can provide us - the better.

*Options classes were tough because of inheritance and covariance (return types). I need to dive in and check and am not sure when I'll be ready to do so. Namely I need to do tons of updates in XamarinComponents which fix several bugs and are crucial for new updates of AndroidX and GPS-FB-MLKit

moljac avatar Feb 26 '21 09:02 moljac

@moljac

Hi! I guess you answered the wrong person :) Anyway thanks for the explanation.

I spent some time playing with build process lately (PR #437). I'll try to write sample which cover my needs in separate PR.

mmarinchenko avatar Mar 03 '21 17:03 mmarinchenko

@mmarinchenko

I'll need to dive deeper into the bindings, but sample would help me a lot.

Bindings are important, but samples are also important to verify the bindings.

I finished tons of updates in XamarinComponents and want to fix something from months ago, before I can make further steps (updates, samples and tooliing)

moljac avatar Apr 18 '21 15:04 moljac

Did you guys have any success with getting the builder options working?

tomkulaga avatar May 29 '21 12:05 tomkulaga

@tomkulaga

Sorry, I'm busy with another project right now. @moljac is busy too, I guess.

mmarinchenko avatar May 31 '21 13:05 mmarinchenko

@mmarinchenko

no problems. Did you every try and compile from source and edit the Additions.cs like the image automl?

I’m trying that but keep getting build errors on latest branch and latest tagged release

tomkulaga avatar May 31 '21 21:05 tomkulaga

Xamarin.Android v11.2 (VS v16.9.*) has issues related to Kotlin metadata (ex. see https://github.com/xamarin/java.interop/issues/826). But I haven't tried VS v16.10 yet.

mmarinchenko avatar Jun 01 '21 06:06 mmarinchenko

I had mode success in 16.10 than 16.11 preview.

It finished the binderate target but bailed on the line.

I opened the generated SLN but it failed on an Android generator step late in the build. I was building MLKIT Obejct Detection and got 10/12 built.

Gave up after that :(

Sent from my iPhone

On 1 Jun 2021, at 4:09 pm, Maxim Marinchenko @.***> wrote:

 Xamarin.Android v11.2 (VS v16.9.*) has issues related to Kotlin metadata (ex. see xamarin/java.interop#826). But I haven't tried VS v16.10 yet.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

tomkulaga avatar Jun 01 '21 07:06 tomkulaga

Any update on this?

alexgg94 avatar Feb 15 '22 17:02 alexgg94

I was able to build them BUT couldn't fix the original issue, so gave up.

tomkulaga avatar Feb 15 '22 21:02 tomkulaga

For those who may hit this issue and can't wait for Google MLKit fix in Xamarin.

ONNX can be used for ML tasks on mobile platforms as an alternative.

Blog post: https://devblogs.microsoft.com/xamarin/machine-learning-in-xamarin-forms-with-onnx-runtime/ Sample: https://github.com/microsoft/onnxruntime-inference-examples/tree/main/mobile/examples/Xamarin

mmarinchenko avatar Mar 10 '22 10:03 mmarinchenko

Were you able to get realtime object detection working with Onnx?

tomkulaga avatar Mar 12 '22 22:03 tomkulaga

No, I haven't tried it yet.

mmarinchenko avatar Mar 13 '22 11:03 mmarinchenko