Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom image labelling module #20

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions custom-image-labeling/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# OSX
#
.DS_Store

# node.js
#
node_modules/
npm-debug.log
yarn-error.log

# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
project.xcworkspace

# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml

# BUCK
buck-out/
\.buckd/
*.keystore
21 changes: 21 additions & 0 deletions custom-image-labeling/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2021 Ahmed Mahmoud

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
31 changes: 31 additions & 0 deletions custom-image-labeling/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# @react-native-ml-kit/custom-image-labeling

React Native On-Device Custom Image Labeling w/ Google ML Kit

## Getting started

`npm install @react-native-ml-kit/custom-image-labeling --save`

### Linking

#### React Native > 0.59

[CLI autolink feature](https://github.com/react-native-community/cli/blob/master/docs/autolinking.md) links the module while building the app.

#### React Native <= 0.59

`react-native link @react-native-ml-kit/custom-image-labeling`

### Installing Pods

On iOS, use CocoaPods to add the native RNMLKitCustomImageLabeling to your project:

`npx pod-install`

## Usage

```javascript
import CustomImageLabeling from '@react-native-ml-kit/custom-image-labeling';

const labels = await CustomImageLabeling.label(imageURL);
```
26 changes: 26 additions & 0 deletions custom-image-labeling/RNMLKitCustomImageLabeling.podspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# RNMLKitCustomImageLabeling.podspec

require "json"

package = JSON.parse(File.read(File.join(__dir__, "package.json")))

Pod::Spec.new do |s|
s.name = "RNMLKitCustomImageLabeling"
s.version = package["version"]
s.summary = package["description"]

s.homepage = "https://github.com/a7med-mahmoud/react-native-ml-kit"
# brief license entry:
s.license = "MIT"
# optional - use expanded license entry instead:
# s.license = { :type => "MIT", :file => "LICENSE" }
s.authors = { "Ahmed" => "[email protected]" }
s.platforms = { :ios => "9.0" }
s.source = { :git => "https://github.com/a7med-mahmoud/react-native-ml-kit.git", :tag => "#{s.version}" }

s.source_files = "ios/**/*.{h,c,cc,cpp,m,mm,swift}"
s.requires_arc = true

s.dependency "React"
s.dependency "GoogleMLKit/ImageLabeling", "2.6.0"
end
4 changes: 4 additions & 0 deletions custom-image-labeling/android/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.iml
gradle/
gradlew
gradlew.bat
10 changes: 10 additions & 0 deletions custom-image-labeling/android/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
.DS_Store
.gradle/
.idea/
.npmignore
build/
gradle/
gradlew
gradlew.bat
local.properties
14 changes: 14 additions & 0 deletions custom-image-labeling/android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
README
======

If you want to publish the lib as a maven dependency, follow these steps before publishing a new version to npm:

1. Be sure to have the Android [SDK](https://developer.android.com/studio/index.html) and [NDK](https://developer.android.com/ndk/guides/index.html) installed
2. Be sure to have a `local.properties` file in this folder that points to the Android SDK and NDK
```
ndk.dir=/Users/{username}/Library/Android/sdk/ndk-bundle
sdk.dir=/Users/{username}/Library/Android/sdk
```
3. Delete the `maven` folder
4. Run `./gradlew installArchives`
5. Verify that latest set of generated files is in the maven folder with the correct version number
65 changes: 65 additions & 0 deletions custom-image-labeling/android/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// These defaults should reflect the SDK versions used by
// the minimum React Native version supported.
def DEFAULT_COMPILE_SDK_VERSION = 28
def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3'
def DEFAULT_MIN_SDK_VERSION = 16
def DEFAULT_TARGET_SDK_VERSION = 28

def safeExtGet(prop, fallback) {
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

apply plugin: 'com.android.library'

buildscript {
// The Android Gradle plugin is only required when opening the android folder stand-alone.
// This avoids unnecessary downloads and potential conflicts when the library is included as a
// module dependency in an application project.
// ref: https://docs.gradle.org/current/userguide/tutorial_using_tasks.html#sec:build_script_external_dependencies
if (project == rootProject) {
repositories {
google()
}
dependencies {
// This should reflect the Gradle plugin version used by
// the minimum React Native version supported.
classpath 'com.android.tools.build:gradle:3.4.1'
}
}
}

android {
compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
defaultConfig {
minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
}

repositories {
mavenCentral()
// ref: https://www.baeldung.com/maven-local-repository
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
maven {
// Android JSC is installed from npm
url "$rootDir/../node_modules/jsc-android/dist"
}
google()
}

dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+' // From node_modules
implementation 'com.google.mlkit:image-labeling:17.0.7'
implementation 'com.google.mlkit:image-labeling-custom:17.0.1'
}
6 changes: 6 additions & 0 deletions custom-image-labeling/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<!-- AndroidManifest.xml -->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rnmlkit.customimagelabeling">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// CustomImageLabelingModule.java

package com.rnmlkit.customimagelabeling;

import android.net.Uri;
import android.content.res.AssetManager;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableMap;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.common.model.LocalModel;
import com.google.mlkit.vision.label.custom.CustomImageLabelerOptions;
import com.google.mlkit.vision.label.ImageLabel;
import com.google.mlkit.vision.label.ImageLabeler;
import com.google.mlkit.vision.label.ImageLabeling;
import com.google.mlkit.vision.label.defaults.ImageLabelerOptions;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.net.URL;

public class CustomImageLabelingModule extends ReactContextBaseJavaModule {

private final ReactApplicationContext reactContext;

public CustomImageLabelingModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}

@Override
public String getName() {
return "CustomImageLabeling";
}

public static List<String> loadLabelsFromAsset(ReactApplicationContext context, String fileName)
throws IOException {

AssetManager manager = context.getAssets();
InputStream is = manager.open(fileName);

List<String> localLabels = new ArrayList<String>();

BufferedReader bf = new BufferedReader(new InputStreamReader(is, "UTF-8"));

String line = bf.readLine();

while (line != null) {
localLabels.add(line);
line = bf.readLine();
}

is.close();
bf.close();

return localLabels;
}

public static InputImage getInputImage(ReactApplicationContext reactContext, String url)
throws IOException {

if (url.contains("http://") || url.contains("https://")) {
URL urlInput = new URL(url);
Bitmap image = BitmapFactory.decodeStream(urlInput.openConnection().getInputStream());
InputImage inputImage = InputImage.fromBitmap(image, 0);
return inputImage;
}
else {
Uri uri = Uri.parse(url);
InputImage inputImage = InputImage.fromFilePath(reactContext, uri);
return inputImage;
}
}

@ReactMethod
public void label(final ReadableMap optionsMap, final Promise promise) {

String url = optionsMap.getString("url");
Float confidence = (float)(optionsMap.getDouble("confidence"));

try {
InputImage image = getInputImage(this.reactContext, url);

String localModelFilename = optionsMap.getString("localModelFilename");
String localLabelsFilename = optionsMap.getString("localLabelsFilename");

List<String> localLabels
= loadLabelsFromAsset(this.reactContext, localLabelsFilename);

LocalModel localModel = new LocalModel.Builder()
.setAssetFilePath(localModelFilename)
.build();

CustomImageLabelerOptions options =
new CustomImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(confidence)
.build();

ImageLabeler labeler = ImageLabeling.getClient(options);

labeler.process(image)
.addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() {
@Override
public void onSuccess(List<ImageLabel> labels) {
WritableArray result = Arguments.createArray();
for (ImageLabel label : labels) {
WritableMap map = Arguments.createMap();
map.putString("text", localLabels.get(label.getIndex()));
map.putDouble("confidence", label.getConfidence());
map.putInt("index", label.getIndex());
result.pushMap(map);
}
promise.resolve(result);
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
e.printStackTrace();
promise.reject("Custom Image labeling failed", e);
}
});
} catch (IOException e) {
e.printStackTrace();
promise.reject("Custom Image labeling failed", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// CustomImageLabelingPackage.java

package com.rnmlkit.customimagelabeling;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

public class CustomImageLabelingPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new CustomImageLabelingModule(reactContext));
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Loading