├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── libs │ └── tesseract4android-release.aar ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── vladih │ └── computer_vision │ └── flutter_vision │ ├── FlutterVisionPlugin.java │ ├── models │ ├── Tesseract.java │ ├── Yolo.java │ ├── Yolov5.java │ ├── Yolov8.java │ └── Yolov8Seg.java │ └── utils │ ├── FeedInputTensorHelper.java │ ├── RenderScriptHelper.java │ └── utils.java ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── vladih │ │ │ │ │ └── computer_vision │ │ │ │ │ └── flutter_vision_example │ │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── assets │ ├── labels.txt │ ├── tessdata │ │ └── spa.traineddata │ ├── tessdata_config.json │ ├── yolov5n.tflite │ ├── yolov8n-seg.tflite │ └── yolov8n.tflite ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ └── main.dart ├── pubspec.lock ├── pubspec.yaml └── test │ └── widget_test.dart ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── FlutterVisionPlugin.h │ ├── FlutterVisionPlugin.m │ └── SwiftFlutterVisionPlugin.swift └── flutter_vision.podspec ├── lib ├── flutter_vision.dart └── src │ └── plugin │ ├── android.dart │ └── base.dart ├── pubspec.yaml └── test └── flutter_vision_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | .vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 25 | /pubspec.lock 26 | **/doc/api/ 27 | .dart_tool/ 28 | .packages 29 | build/ 30 | .fvm -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 7e9793dee1b85a243edd0e06cb1658e98b077561 8 | channel: stable 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.1.4 2 | * Resolved the YoloV8 output bug and made updates to the latest version. Please find the latest release on the Ultralytics YOLOv8 0.181 GitHub repository 3 | ## 1.1.3 4 | * Release of segmentation feature via YOLOv8. 5 | * Updated example code. 6 | * Updated README. 7 | ## 1.1.2 8 | * GPU delegation error has been fixed. 9 | * Coordinate representation of the box in documentation was fixed. 10 | * Switching between models now are supported. 11 | * Added quantization option for more efficient models at the cost of some precision. 12 | ## 1.1.1 13 | * Bounding box error has been fixed. 14 | * Confidence scores for Yolov8 has been fixed. 15 | ## 1.1.0 16 | * loadOcrModel, ocrOnFrame, and closeOcrModel have been removed. Instead, Yolo and Tesseract operate independently of each other. 17 | * Models no longer returns responseHandler as output. Instead, it returns a List>. 18 | * The Tesseract model has been updated to version 5.0.0, resulting in improved accuracy. 19 | * New methods have been added: loadYoloModel, yoloOnFrame, yoloOnImage, closeYoloModel, loadTesseractModel, tesseractOnImage, and closeTesseractModel. 20 | * Support is now available for both Yolov5 and Yolov8. 21 | * Resource management has been improved, and all models now operate in the background. 22 | 23 | ## 1.0.0 24 | * Methods for Yolov5 now is available `(loadYoloModel, yoloOnFrame, closeYoloModel)`. 25 | * Yolov5 and OCR model now is independent one to each other. 26 | 27 | ## 0.0.2 28 | * `best` parameter has been removed 29 | 30 | ## 0.0.1 31 | * Initial release. 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yuri Vladimir Huallpa Vargas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_vision 2 | 3 | A Flutter plugin for managing [Yolov5, Yolov8](https://github.com/ultralytics/ultralytics) and [Tesseract v5](https://tesseract-ocr.github.io/tessdoc/) accessing with TensorFlow Lite 2.x. Support object detection, segmentation and OCR on Android. iOS not updated, working in progress. 4 | 5 | # Installation 6 | Add flutter_vision as a dependency in your pubspec.yaml file. 7 | 8 | ## Android 9 | In `android/app/build.gradle`, add the following setting in android block. 10 | 11 | ```gradle 12 | android{ 13 | aaptOptions { 14 | noCompress 'tflite' 15 | noCompress 'lite' 16 | } 17 | } 18 | ``` 19 | ## iOS 20 | Comming soon ... 21 | 22 | # Usage 23 | ## For YoloV5 and YoloV8 MODEL 24 | 1. Create a `assets` folder and place your labels file and model file in it. In `pubspec.yaml` add: 25 | 26 | ``` 27 | assets: 28 | - assets/labels.txt 29 | - assets/yolovx.tflite 30 | ``` 31 | 32 | 2. Import the library: 33 | 34 | ```dart 35 | import 'package:flutter_vision/flutter_vision.dart'; 36 | ``` 37 | 38 | 3. Initialized the flutter_vision library: 39 | 40 | ```dart 41 | FlutterVision vision = FlutterVision(); 42 | ``` 43 | 44 | 4. Load the model and labels: 45 | `modelVersion`: yolov5 or yolov8 or yolov8seg 46 | ```dart 47 | await vision.loadYoloModel( 48 | labels: 'assets/labelss.txt', 49 | modelPath: 'assets/yolov5n.tflite', 50 | modelVersion: "yolov5", 51 | quantization: false, 52 | numThreads: 1, 53 | useGpu: false); 54 | ``` 55 | ### For camera live feed 56 | 5. Make your first detection: 57 | `confThreshold` work with yolov5 other case it is omited. 58 | > _Make use of [camera plugin](https://pub.dev/packages/camera)_ 59 | 60 | ```dart 61 | final result = await vision.yoloOnFrame( 62 | bytesList: cameraImage.planes.map((plane) => plane.bytes).toList(), 63 | imageHeight: cameraImage.height, 64 | imageWidth: cameraImage.width, 65 | iouThreshold: 0.4, 66 | confThreshold: 0.4, 67 | classThreshold: 0.5); 68 | ``` 69 | 70 | ### For static image 71 | 5. Make your first detection or segmentation: 72 | 73 | ```dart 74 | final result = await vision.yoloOnImage( 75 | bytesList: byte, 76 | imageHeight: image.height, 77 | imageWidth: image.width, 78 | iouThreshold: 0.8, 79 | confThreshold: 0.4, 80 | classThreshold: 0.7); 81 | ``` 82 | 83 | 6. Release resources: 84 | 85 | ```dart 86 | await vision.closeYoloModel(); 87 | ``` 88 | ## For Tesseract 5.0.0 MODEL 89 | 1. Create an `assets` folder, then create a `tessdata` directory and `tessdata_config.json` file and place them into it. 90 | Download trained data for tesseract from [here](https://github.com/tesseract-ocr/tessdata) and place it into tessdata directory. Then, modifie tessdata_config.json as follow. 91 | ```json 92 | { 93 | "files": [ 94 | "spa.traineddata" 95 | ] 96 | } 97 | ``` 98 | 99 | 2. In `pubspec.yaml` add: 100 | ``` 101 | assets: 102 | - assets/ 103 | - assets/tessdata/ 104 | ``` 105 | 3. Import the library: 106 | 107 | ```dart 108 | import 'package:flutter_vision/flutter_vision.dart'; 109 | ``` 110 | 111 | 4. Initialized the flutter_vision library: 112 | 113 | ```dart 114 | FlutterVision vision = FlutterVision(); 115 | ``` 116 | 117 | 5. Load the model: 118 | 119 | ```dart 120 | await vision.loadTesseractModel( 121 | args: { 122 | 'psm': '11', 123 | 'oem': '1', 124 | 'preserve_interword_spaces': '1', 125 | }, 126 | language: 'spa', 127 | ); 128 | ``` 129 | 130 | ### For static image 131 | 6. Get Text from static image: 132 | 133 | ```dart 134 | final XFile? photo = await picker.pickImage(source: ImageSource.gallery); 135 | if (photo != null) { 136 | final result = await vision.tesseractOnImage(bytesList: (await photo.readAsBytes())); 137 | } 138 | ``` 139 | 140 | 7. Release resources: 141 | 142 | ```dart 143 | await vision.closeTesseractModel(); 144 | ``` 145 | # About results 146 | ## For Yolo v5 or v8 in detection task 147 | result is a `List>` where Map have the following keys: 148 | 149 | ``` dart 150 | Map:{ 151 | "box": [x1:left, y1:top, x2:right, y2:bottom, class_confidence] 152 | "tag": String: detected class 153 | } 154 | ``` 155 | 156 | ## For YoloV8 in segmentation task 157 | result is a `List>` where Map have the following keys: 158 | 159 | ``` dart 160 | Map:{ 161 | "box": [x1:left, y1:top, x2:right, y2:bottom, class_confidence] 162 | "tag": String: detected class 163 | "polygons": List>: [{x:coordx, y:coordy}] 164 | } 165 | ``` 166 | 167 | ## For Tesseract 168 | result is a `List>` where Map have the following keys: 169 | 170 | ```dart 171 | Map:{ 172 | "text": String 173 | "word_conf": List:int 174 | "mean_conf": int} 175 | ``` 176 | 177 | # Example 178 | ![Screenshot_2022-04-08-23-59-05-652_com vladih dni_scanner_example](https://user-images.githubusercontent.com/32783435/164163922-2eb7c8a3-8415-491f-883e-12cc87512efe.jpg) 179 | Home 180 | Detection 181 | Segmentation 182 | 183 | 184 | #
Contact
185 | 186 | For flutter_vision bug reports and feature requests please visit [GitHub Issues](https://github.com/vladiH/flutter_vision/issues) 187 | 188 |
189 |
190 | 191 | 192 |
193 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'com.vladih.computer_vision.flutter_vision' 2 | version '1.0' 3 | 4 | buildscript { 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:7.1.2' 12 | } 13 | } 14 | 15 | rootProject.allprojects { 16 | repositories { 17 | google() 18 | mavenCentral() 19 | flatDir{ 20 | dirs project(":flutter_vision").file("libs") 21 | } 22 | maven { 23 | url 'https://jitpack.io' 24 | } 25 | maven{ 26 | name 'ossrh-snapshot' 27 | url 'https://oss.sonatype.org/content/repositories/snapshots' 28 | } 29 | } 30 | } 31 | 32 | apply plugin: 'com.android.library' 33 | 34 | android { 35 | compileSdkVersion 31 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_1_8 39 | targetCompatibility JavaVersion.VERSION_1_8 40 | } 41 | 42 | defaultConfig { 43 | minSdkVersion 21 44 | } 45 | aaptOptions { 46 | noCompress 'tflite' 47 | noCompress 'lite' 48 | } 49 | 50 | buildFeatures{ 51 | mlModelBinding true 52 | } 53 | } 54 | dependencies{ 55 | //implementation (files('libs/tesseract4android-release.aar')) 56 | api(name:"tesseract4android-release", ext: "aar") 57 | implementation 'com.github.vladiH:opencv-android:v1.0.0' 58 | implementation 'org.tensorflow:tensorflow-lite:2.10.0' 59 | implementation 'org.tensorflow:tensorflow-lite-api:2.10.0' 60 | implementation 'org.tensorflow:tensorflow-lite-gpu:2.10.0' 61 | implementation 'org.tensorflow:tensorflow-lite-gpu-api:2.10.0' 62 | implementation 'org.tensorflow:tensorflow-lite-gpu-delegate-plugin:0.4.3' 63 | implementation 'org.tensorflow:tensorflow-lite-support:0.4.3' 64 | implementation 'org.tensorflow:tensorflow-lite-metadata:0.4.3' 65 | implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:2.11.0' 66 | } -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Feb 22 11:31:00 CET 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/libs/tesseract4android-release.aar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/android/libs/tesseract4android-release.aar -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = "flutter_vision" -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/FlutterVisionPlugin.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Matrix; 7 | 8 | import androidx.annotation.NonNull; 9 | 10 | import com.vladih.computer_vision.flutter_vision.models.Tesseract; 11 | import com.vladih.computer_vision.flutter_vision.models.Yolo; 12 | import com.vladih.computer_vision.flutter_vision.models.Yolov8; 13 | import com.vladih.computer_vision.flutter_vision.models.Yolov5; 14 | import com.vladih.computer_vision.flutter_vision.models.Yolov8Seg; 15 | import com.vladih.computer_vision.flutter_vision.utils.utils; 16 | 17 | import org.opencv.android.OpenCVLoader; 18 | import org.opencv.android.Utils; 19 | import org.opencv.core.Mat; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.concurrent.ExecutorService; 26 | import java.util.concurrent.Executors; 27 | 28 | import io.flutter.embedding.engine.plugins.FlutterPlugin; 29 | import io.flutter.plugin.common.BinaryMessenger; 30 | import io.flutter.plugin.common.MethodCall; 31 | import io.flutter.plugin.common.MethodChannel; 32 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler; 33 | import io.flutter.plugin.common.MethodChannel.Result; 34 | 35 | /** 36 | * FlutterVisionPlugin 37 | */ 38 | public class FlutterVisionPlugin implements FlutterPlugin, MethodCallHandler { 39 | private static final String CHANNEL_NAME = "flutter_vision"; 40 | private MethodChannel methodChannel; 41 | private Context context; 42 | private FlutterAssets assets; 43 | private Yolo yolo_model; 44 | private Tesseract tesseract_model; 45 | 46 | private ExecutorService executor; 47 | 48 | private boolean isDetecting = false; 49 | 50 | private static ArrayList> empty = new ArrayList<>(); 51 | 52 | @Override 53 | public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { 54 | setupChannel(binding.getApplicationContext(), binding.getFlutterAssets(), binding.getBinaryMessenger()); 55 | } 56 | 57 | @Override 58 | public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { 59 | try { 60 | this.context = null; 61 | this.methodChannel.setMethodCallHandler(null); 62 | this.methodChannel = null; 63 | this.assets = null; 64 | close_tesseract(); 65 | close_yolo(); 66 | this.executor.shutdownNow(); 67 | } catch (Exception e) { 68 | if (!this.executor.isShutdown()) { 69 | this.executor.shutdownNow(); 70 | } 71 | // System.out.println(e.getMessage()); 72 | } 73 | } 74 | 75 | private void setupChannel(Context context, FlutterAssets assets, BinaryMessenger messenger) { 76 | OpenCVLoader.initDebug(); 77 | this.assets = assets; 78 | this.context = context; 79 | this.methodChannel = new MethodChannel(messenger, CHANNEL_NAME); 80 | this.methodChannel.setMethodCallHandler(this); 81 | this.executor = Executors.newSingleThreadExecutor(); 82 | } 83 | 84 | @Override 85 | public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) { 86 | // Handle method calls from Flutter 87 | if (call.method.equals("loadOcrModel")) { 88 | try { 89 | load_ocr_model((Map) call.arguments); 90 | } catch (Exception e) { 91 | result.error("100", "Error on load ocr components", e); 92 | } 93 | } else if (call.method.equals("ocrOnFrame")) { 94 | ocr_on_frame((Map) call.arguments, result); 95 | } else if (call.method.equals("closeOcrModel")) { 96 | close_ocr_model(result); 97 | } else if (call.method.equals("loadYoloModel")) { 98 | try { 99 | load_yolo_model((Map) call.arguments); 100 | result.success("ok"); 101 | } catch (Exception e) { 102 | result.error("100", "Error on load Yolov5 model", e); 103 | } 104 | } else if (call.method.equals("yoloOnFrame")) { 105 | yolo_on_frame((Map) call.arguments, result); 106 | } else if (call.method.equals("yoloOnImage")) { 107 | yolo_on_image((Map) call.arguments, result); 108 | } else if (call.method.equals("closeYoloModel")) { 109 | close_yolo_model(result); 110 | } else if (call.method.equals("loadTesseractModel")) { 111 | try { 112 | load_tesseract_model((Map) call.arguments); 113 | result.success("ok"); 114 | } catch (Exception e) { 115 | result.error("100", "Error on load Tesseract model", e); 116 | } 117 | } else if (call.method.equals("tesseractOnImage")) { 118 | tesseract_on_image((Map) call.arguments, result); 119 | } else if (call.method.equals("closeTesseractModel")) { 120 | close_tesseract_model(result); 121 | } else { 122 | result.notImplemented(); 123 | } 124 | } 125 | 126 | private void load_ocr_model(Map args) throws Exception { 127 | load_yolo_model(args); 128 | load_tesseract_model(args); 129 | } 130 | 131 | private void ocr_on_frame(Map args, Result result) { 132 | try { 133 | List image = (ArrayList) args.get("bytesList"); 134 | int image_height = (int) args.get("image_height"); 135 | int image_width = (int) args.get("image_width"); 136 | float iou_threshold = (float) (double) (args.get("iou_threshold")); 137 | float conf_threshold = (float) (double) (args.get("conf_threshold")); 138 | float class_threshold = (float) (double) (args.get("class_threshold")); 139 | List class_is_text = (List) args.get("class_is_text"); 140 | Bitmap bitmap = utils.feedInputToBitmap(context.getApplicationContext(), image, image_height, image_width, 90); 141 | int[] shape = yolo_model.getInputTensor().shape(); 142 | ByteBuffer byteBuffer = utils.feedInputTensor(bitmap, shape[1], shape[2], image_width, image_height, 0, 255); 143 | 144 | List> yolo_results = yolo_model.detect_task(byteBuffer, image_height, image_width, iou_threshold, conf_threshold, class_threshold); 145 | for (Map yolo_result : yolo_results) { 146 | float[] box = (float[]) yolo_result.get("box"); 147 | if (class_is_text.contains((int) box[5])) { 148 | Bitmap crop = utils.crop_bitmap(bitmap, 149 | box[0], box[1], box[2], box[3]); 150 | //utils.getScreenshotBmp(crop, "crop"); 151 | Bitmap tmp = crop.copy(crop.getConfig(), crop.isMutable()); 152 | yolo_result.put("text", tesseract_model.predict_text(tmp)); 153 | } else { 154 | yolo_result.put("text", ""); 155 | } 156 | } 157 | result.success(yolo_results); 158 | } catch (Exception e) { 159 | result.error("100", "Ocr error", e); 160 | } 161 | } 162 | 163 | private void close_ocr_model(Result result) { 164 | try { 165 | close_tesseract(); 166 | close_yolo(); 167 | result.success("OCR model closed succesfully"); 168 | } catch (Exception e) { 169 | result.error("100", "Fail closed ocr model", e); 170 | } 171 | } 172 | 173 | private void load_yolo_model(Map args) throws Exception { 174 | final String model = this.assets.getAssetFilePathByName(args.get("model_path").toString()); 175 | final Object is_asset_obj = args.get("is_asset"); 176 | final boolean is_asset = is_asset_obj == null ? false : (boolean) is_asset_obj; 177 | final int num_threads = (int) args.get("num_threads"); 178 | final boolean quantization = (boolean) args.get("quantization"); 179 | final boolean use_gpu = (boolean) args.get("use_gpu"); 180 | final String label_path = this.assets.getAssetFilePathByName(args.get("label_path").toString()); 181 | final int rotation = (int) args.get("rotation"); 182 | final String version = args.get("model_version").toString(); 183 | switch (version) { 184 | case "yolov5": { 185 | yolo_model = new Yolov5( 186 | context, 187 | model, 188 | is_asset, 189 | num_threads, 190 | quantization, 191 | use_gpu, 192 | label_path, 193 | rotation); 194 | break; 195 | } 196 | case "yolov8": { 197 | yolo_model = new Yolov8( 198 | context, 199 | model, 200 | is_asset, 201 | num_threads, 202 | quantization, 203 | use_gpu, 204 | label_path, 205 | rotation); 206 | break; 207 | } 208 | 209 | case "yolov8seg": { 210 | yolo_model = new Yolov8Seg( 211 | context, 212 | model, 213 | is_asset, 214 | num_threads, 215 | quantization, 216 | use_gpu, 217 | label_path, 218 | rotation); 219 | break; 220 | } 221 | default: { 222 | throw new Exception("Model version must be yolov5, yolov8 or yolov8seg"); 223 | } 224 | } 225 | yolo_model.initialize_model(); 226 | } 227 | 228 | //https://www.baeldung.com/java-single-thread-executor-service 229 | class DetectionTask implements Runnable { 230 | // private static volatile DetectionTasks instance; 231 | private Yolo yolo; 232 | byte[] image; 233 | 234 | List frame; 235 | int image_height; 236 | int image_width; 237 | float iou_threshold; 238 | float conf_threshold; 239 | float class_threshold; 240 | 241 | String typing; 242 | private Result result; 243 | 244 | public DetectionTask(Yolo yolo, Map args, String typing, Result result) { 245 | this.typing = typing; 246 | this.yolo = yolo; 247 | if (typing == "img") { 248 | this.image = (byte[]) args.get("bytesList"); 249 | } else { 250 | this.frame = (ArrayList) args.get("bytesList"); 251 | } 252 | this.image_height = (int) args.get("image_height"); 253 | this.image_width = (int) args.get("image_width"); 254 | this.iou_threshold = (float) (double) (args.get("iou_threshold")); 255 | this.conf_threshold = (float) (double) (args.get("conf_threshold")); 256 | this.class_threshold = (float) (double) (args.get("class_threshold")); 257 | this.result = result; 258 | } 259 | @Override 260 | public void run() { 261 | try { 262 | Bitmap bitmap; 263 | if (typing == "img") { 264 | bitmap = BitmapFactory.decodeByteArray(image, 0, image.length); 265 | } else { 266 | //rotate image, because android take a photo rotating 90 degrees 267 | bitmap = utils.feedInputToBitmap(context, frame, image_height, image_width, 90); 268 | } 269 | int[] shape = yolo.getInputTensor().shape(); 270 | int src_width = bitmap.getWidth(); 271 | int src_height = bitmap.getHeight(); 272 | ByteBuffer byteBuffer = utils.feedInputTensor(bitmap, shape[1], shape[2], src_width, src_height, 0, 255); 273 | List> detections = yolo.detect_task(byteBuffer, src_height, src_width, iou_threshold, conf_threshold, class_threshold); 274 | isDetecting = false; 275 | result.success(detections); 276 | } catch (Exception e) { 277 | result.error("100", "Detection Error", e); 278 | } 279 | } 280 | } 281 | 282 | private void yolo_on_frame(Map args, Result result) { 283 | try { 284 | if (isDetecting) { 285 | result.success(empty); 286 | } else { 287 | isDetecting = true; 288 | DetectionTask detectionTask = new DetectionTask(yolo_model, args, "frame", result); 289 | executor.submit(detectionTask); 290 | } 291 | } catch (Exception e) { 292 | result.error("100", "Detection Error", e); 293 | } 294 | } 295 | 296 | private void yolo_on_image(Map args, Result result) { 297 | try { 298 | if (isDetecting) { 299 | result.success(empty); 300 | } else { 301 | isDetecting = true; 302 | DetectionTask detectionTask = new DetectionTask(yolo_model, args, "img", result); 303 | executor.submit(detectionTask); 304 | } 305 | } catch (Exception e) { 306 | result.error("100", "Detection Error", e); 307 | } 308 | } 309 | 310 | private void close_yolo_model(Result result) { 311 | try { 312 | close_yolo(); 313 | result.success("Yolo model closed succesfully"); 314 | } catch (Exception e) { 315 | result.error("100", "Close_yolo_model error", e); 316 | } 317 | } 318 | 319 | private void load_tesseract_model(Map args) throws Exception { 320 | final String tess_data = args.get("tess_data").toString(); 321 | final Map arg = (Map) args.get("arg"); 322 | final String language = args.get("language").toString(); 323 | tesseract_model = new Tesseract(tess_data, arg, language); 324 | tesseract_model.initialize_model(); 325 | } 326 | 327 | class PredictionTask implements Runnable { 328 | private Tesseract tesseract; 329 | private Bitmap bitmap; 330 | private Result result; 331 | 332 | public PredictionTask(Tesseract tesseract, Map args, Result result) { 333 | byte[] image = (byte[]) args.get("bytesList"); 334 | this.tesseract = tesseract; 335 | this.bitmap = BitmapFactory.decodeByteArray(image, 0, image.length); 336 | this.result = result; 337 | } 338 | 339 | @Override 340 | public void run() { 341 | try { 342 | Mat mat = utils.rgbBitmapToMatGray(bitmap); 343 | double angle = utils.computeSkewAngle(mat.clone()); 344 | mat = utils.deskew(mat, angle); 345 | mat = utils.filterTextFromImage(mat); 346 | bitmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888); 347 | Utils.matToBitmap(mat, bitmap); 348 | // utils.getScreenshotBmp(bitmap,"TESSEREACT"); 349 | result.success(tesseract.predict_text(bitmap)); 350 | } catch (Exception e) { 351 | result.error("100", "Prediction text Error", e); 352 | } 353 | } 354 | } 355 | 356 | private void tesseract_on_image(Map args, Result result) { 357 | try { 358 | PredictionTask predictionTask = new PredictionTask(tesseract_model, args, result); 359 | executor.submit(predictionTask); 360 | } catch (Exception e) { 361 | result.error("100", "Prediction Error", e); 362 | } 363 | } 364 | 365 | private void close_tesseract_model(Result result) { 366 | try { 367 | close_tesseract(); 368 | result.success("Tesseract model closed succesfully"); 369 | } catch (Exception e) { 370 | result.error("100", "close_tesseract_model error", e); 371 | } 372 | } 373 | 374 | private void close_tesseract(){ 375 | if (tesseract_model != null) { 376 | tesseract_model.close(); 377 | tesseract_model = null; 378 | } 379 | } 380 | 381 | private void close_yolo(){ 382 | if (yolo_model != null) { 383 | yolo_model.close(); 384 | yolo_model = null; 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Tesseract.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.models; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | import com.googlecode.tesseract.android.TessBaseAPI; 6 | 7 | import java.util.ArrayList; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | import java.util.Vector; 12 | 13 | public class Tesseract { 14 | private TessBaseAPI interpreter; 15 | private final int default_page_seg_mode = TessBaseAPI.PageSegMode.PSM_SINGLE_BLOCK; 16 | private final String tess_data; 17 | private final Map arg; 18 | private final String language; 19 | 20 | public Tesseract(String tess_data, Map arg, String language) { 21 | this.tess_data = tess_data; 22 | this.arg = arg; 23 | this.language = language; 24 | } 25 | public void close(){ 26 | if (interpreter!=null){ 27 | interpreter.clear(); 28 | interpreter.recycle(); 29 | } 30 | } 31 | public void initialize_model() throws Exception { 32 | try { 33 | if(interpreter==null){ 34 | this.interpreter = new TessBaseAPI(); 35 | if (!this.interpreter.init(this.tess_data, this.language)) { 36 | // Error initializing Tesseract (wrong data path or language) 37 | this.interpreter.recycle(); 38 | throw new Exception("Cannot initialize Tesseract model"); 39 | } 40 | if(!this.arg.isEmpty()){ 41 | for(Map.Entry entry:this.arg.entrySet()){ 42 | interpreter.setVariable(entry.getKey(),entry.getValue()); 43 | } 44 | } 45 | interpreter.setPageSegMode(this.default_page_seg_mode); 46 | } 47 | } 48 | catch (Exception e){ 49 | throw e; 50 | } 51 | } 52 | 53 | public Map predict_text(Bitmap bitmap) throws Exception { 54 | try{ 55 | this.interpreter.setImage(bitmap); 56 | String result = this.interpreter.getUTF8Text(); 57 | return out(result, interpreter.meanConfidence(), interpreter.wordConfidences()); 58 | }catch (Exception e){ 59 | throw new Exception(e.getMessage()); 60 | }finally { 61 | if(!bitmap.isRecycled()){ 62 | bitmap.recycle(); 63 | } 64 | } 65 | } 66 | 67 | protected Map out(String text, int mean, int[] word_conf){ 68 | try { 69 | Map result = new HashMap(); 70 | result.put("text",text); 71 | result.put("mean_conf", mean); 72 | result.put("word_conf",word_conf); 73 | return result; 74 | }catch (Exception e){ 75 | throw e; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolo.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.models; 2 | 3 | import static java.lang.Math.min; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.content.Context; 7 | import android.content.res.AssetFileDescriptor; 8 | import android.content.res.AssetManager; 9 | import android.util.Log; 10 | 11 | import com.vladih.computer_vision.flutter_vision.utils.FeedInputTensorHelper; 12 | 13 | import org.opencv.core.CvType; 14 | import org.opencv.core.Mat; 15 | import org.tensorflow.lite.Interpreter; 16 | import org.tensorflow.lite.Tensor; 17 | import org.tensorflow.lite.gpu.CompatibilityList; 18 | import org.tensorflow.lite.gpu.GpuDelegate; 19 | import org.tensorflow.lite.gpu.GpuDelegateFactory; 20 | 21 | import java.io.BufferedReader; 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.InputStreamReader; 25 | import java.lang.reflect.Array; 26 | import java.nio.ByteBuffer; 27 | import java.nio.MappedByteBuffer; 28 | import java.nio.channels.FileChannel; 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.Comparator; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Vector; 36 | 37 | import io.flutter.embedding.engine.FlutterEngine; 38 | import io.flutter.embedding.engine.plugins.FlutterPlugin; 39 | 40 | public class Yolo { 41 | protected float[][][] output; 42 | protected Interpreter interpreter; 43 | protected Vector labels; 44 | protected final Context context; 45 | protected final String model_path; 46 | protected final boolean is_assets; 47 | protected final int num_threads; 48 | protected final boolean quantization; 49 | protected final boolean use_gpu; 50 | protected final String label_path; 51 | protected final int rotation; 52 | 53 | public Yolo(Context context, 54 | String model_path, 55 | boolean is_assets, 56 | int num_threads, 57 | boolean quantization, 58 | boolean use_gpu, 59 | String label_path, 60 | int rotation) { 61 | this.context = context; 62 | this.model_path = model_path; 63 | this.is_assets = is_assets; 64 | this.num_threads = num_threads; 65 | this.quantization = quantization; 66 | this.use_gpu = use_gpu; 67 | this.label_path = label_path; 68 | this.rotation = rotation; 69 | } 70 | 71 | // public Vector getLabels(){return this.labels;} 72 | public Tensor getInputTensor() { 73 | return this.interpreter.getInputTensor(0); 74 | } 75 | 76 | @SuppressLint("SuspiciousIndentation") 77 | public void initialize_model() throws Exception { 78 | AssetManager asset_manager = null; 79 | MappedByteBuffer buffer = null; 80 | FileChannel file_channel = null; 81 | FileInputStream input_stream = null; 82 | 83 | try { 84 | if (is_assets) { 85 | asset_manager = context.getAssets(); 86 | AssetFileDescriptor file_descriptor = asset_manager.openFd(this.model_path); 87 | input_stream = new FileInputStream(file_descriptor.getFileDescriptor()); 88 | 89 | file_channel = input_stream.getChannel(); 90 | buffer = file_channel.map( 91 | FileChannel.MapMode.READ_ONLY, file_descriptor.getStartOffset(), 92 | file_descriptor.getLength() 93 | ); 94 | file_descriptor.close(); 95 | } else { 96 | input_stream = new FileInputStream(new File(this.model_path)); 97 | file_channel = input_stream.getChannel(); 98 | buffer = file_channel.map(FileChannel.MapMode.READ_ONLY, 0, file_channel.size()); 99 | } 100 | 101 | Interpreter.Options interpreterOptions = new Interpreter.Options(); 102 | try { 103 | // Check if GPU support is available 104 | CompatibilityList compatibilityList = new CompatibilityList(); 105 | if (use_gpu && compatibilityList.isDelegateSupportedOnThisDevice()) { 106 | GpuDelegateFactory.Options delegateOptions = compatibilityList.getBestOptionsForThisDevice(); 107 | GpuDelegate gpuDelegate = new GpuDelegate(delegateOptions.setQuantizedModelsAllowed(this.quantization)); 108 | interpreterOptions.addDelegate(gpuDelegate); 109 | } else { 110 | interpreterOptions.setNumThreads(num_threads); 111 | } 112 | // Create the interpreter 113 | this.interpreter = new Interpreter(buffer, interpreterOptions); 114 | } catch (Exception e) { 115 | interpreterOptions = new Interpreter.Options(); 116 | interpreterOptions.setNumThreads(num_threads); 117 | // Create the interpreter 118 | this.interpreter = new Interpreter(buffer, interpreterOptions); 119 | } 120 | this.interpreter.allocateTensors(); 121 | this.labels = load_labels(asset_manager, label_path); 122 | int[] shape = interpreter.getOutputTensor(0).shape();//3dimension 123 | this.output = (float [][][]) Array.newInstance(float.class, shape); 124 | } catch (Exception e) { 125 | throw e; 126 | } finally { 127 | if (buffer != null) 128 | buffer.clear(); 129 | if (file_channel != null && file_channel.isOpen()) { 130 | file_channel.close(); 131 | input_stream.close(); 132 | } 133 | } 134 | } 135 | 136 | protected Vector load_labels(AssetManager asset_manager, String label_path) throws Exception { 137 | BufferedReader br = null; 138 | try { 139 | if (asset_manager != null) { 140 | br = new BufferedReader(new InputStreamReader(asset_manager.open(label_path))); 141 | } else { 142 | br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(label_path)))); 143 | } 144 | String line; 145 | Vector labels = new Vector<>(); 146 | while ((line = br.readLine()) != null) { 147 | labels.add(line); 148 | } 149 | return labels; 150 | } catch (Exception e) { 151 | throw new Exception(e.getMessage()); 152 | } finally { 153 | if (br != null) { 154 | br.close(); 155 | } 156 | } 157 | } 158 | 159 | public List> detect_task(ByteBuffer byteBuffer, 160 | int source_height, 161 | int source_width, 162 | float iou_threshold, 163 | float conf_threshold, float class_threshold) throws Exception { 164 | try { 165 | int[] input_shape = this.interpreter.getInputTensor(0).shape(); 166 | this.interpreter.run(byteBuffer, this.output); 167 | List boxes = filter_box(this.output, iou_threshold, conf_threshold, 168 | class_threshold, input_shape[1], input_shape[2]); 169 | boxes = restore_size(boxes, input_shape[1], input_shape[2], source_width, source_height); 170 | return out(boxes, this.labels); 171 | } catch (Exception e) { 172 | throw e; 173 | } finally { 174 | byteBuffer.clear(); 175 | } 176 | } 177 | 178 | protected List filter_box(float[][][] model_outputs, float iou_threshold, 179 | float conf_threshold, float class_threshold, float input_width, float input_height) { 180 | try { 181 | //model_outputs = [1,box+model_conf+class,detected_box] 182 | List pre_box = new ArrayList<>(); 183 | int conf_index = 4; 184 | int class_index = 5; 185 | int dimension = model_outputs[0][0].length; 186 | int rows = model_outputs[0].length; 187 | float x1, y1, x2, y2, conf; 188 | int max_index = 0; 189 | float max = 0f; 190 | for (int i = 0; i < rows; i++) { 191 | //convert xywh to xyxy 192 | x1 = (model_outputs[0][i][0] - model_outputs[0][i][2] / 2f) * input_width; 193 | y1 = (model_outputs[0][i][1] - model_outputs[0][i][3] / 2f) * input_height; 194 | x2 = (model_outputs[0][i][0] + model_outputs[0][i][2] / 2f) * input_width; 195 | y2 = (model_outputs[0][i][1] + model_outputs[0][i][3] / 2f) * input_height; 196 | conf = model_outputs[0][i][conf_index]; 197 | if (conf < conf_threshold) continue; 198 | 199 | max_index = class_index; 200 | max = model_outputs[0][i][max_index]; 201 | 202 | for (int j = class_index + 1; j < dimension; j++) { 203 | float current = model_outputs[0][i][j]; 204 | if (current > max) { 205 | max = current; 206 | max_index = j; 207 | } 208 | } 209 | if (max > class_threshold){ 210 | float[] tmp = new float[6]; 211 | tmp[0] = x1; 212 | tmp[1] = y1; 213 | tmp[2] = x2; 214 | tmp[3] = y2; 215 | tmp[4] = model_outputs[0][i][max_index]; 216 | tmp[5] = (max_index - class_index) * 1f; 217 | pre_box.add(tmp); 218 | } 219 | } 220 | if (pre_box.isEmpty()) return new ArrayList<>(); 221 | //for reverse orden, insteand of using .reversed method 222 | Comparator compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]); 223 | //Collections.sort(pre_box,compareValues.reversed()); 224 | Collections.sort(pre_box, compareValues); 225 | return nms(pre_box, iou_threshold); 226 | } catch (Exception e) { 227 | throw e; 228 | } 229 | } 230 | 231 | protected static List nms(List boxes, float iou_threshold) { 232 | try { 233 | List filteredBoxes = new ArrayList<>(boxes); // Create a copy of the input list 234 | 235 | for (int i = 0; i < filteredBoxes.size(); i++) { 236 | float[] box = filteredBoxes.get(i); 237 | for (int j = i + 1; j < filteredBoxes.size(); j++) { 238 | float[] next_box = filteredBoxes.get(j); 239 | float x1 = Math.max(next_box[0], box[0]); 240 | float y1 = Math.max(next_box[1], box[1]); 241 | float x2 = Math.min(next_box[2], box[2]); 242 | float y2 = Math.min(next_box[3], box[3]); 243 | 244 | float width = Math.max(0, x2 - x1); 245 | float height = Math.max(0, y2 - y1); 246 | 247 | float intersection = width * height; 248 | float union = (next_box[2] - next_box[0]) * (next_box[3] - next_box[1]) 249 | + (box[2] - box[0]) * (box[3] - box[1]) - intersection; 250 | float iou = intersection / union; 251 | if (iou > iou_threshold) { 252 | filteredBoxes.remove(j); 253 | j--; 254 | } 255 | } 256 | } 257 | return filteredBoxes; 258 | } catch (Exception e) { 259 | Log.e("nms", e.getMessage()); 260 | throw e; 261 | } 262 | } 263 | 264 | protected List restore_size(List nms, 265 | int input_width, 266 | int input_height, 267 | int src_width, 268 | int src_height) { 269 | try { 270 | //restore size after scaling, larger images 271 | if (src_width > input_width || src_height > input_height) { 272 | float gainx = src_width / (float) input_width; 273 | float gainy = src_height / (float) input_height; 274 | for (int i = 0; i < nms.size(); i++) { 275 | nms.get(i)[0] = min(src_width, Math.max(nms.get(i)[0] * gainx, 0)); 276 | nms.get(i)[1] = min(src_height, Math.max(nms.get(i)[1] * gainy, 0)); 277 | nms.get(i)[2] = min(src_width, Math.max(nms.get(i)[2] * gainx, 0)); 278 | nms.get(i)[3] = min(src_height, Math.max(nms.get(i)[3] * gainy, 0)); 279 | } 280 | //restore size after padding, smaller images 281 | } else { 282 | float padx = (src_width - input_width) / 2f; 283 | float pady = (src_height - input_height) / 2f; 284 | for (int i = 0; i < nms.size(); i++) { 285 | nms.get(i)[0] = min(src_width, Math.max(nms.get(i)[0] + padx, 0)); 286 | nms.get(i)[1] = min(src_height, Math.max(nms.get(i)[1] + pady, 0)); 287 | nms.get(i)[2] = min(src_width, Math.max(nms.get(i)[2] + padx, 0)); 288 | nms.get(i)[3] = min(src_height, Math.max(nms.get(i)[3] + pady, 0)); 289 | } 290 | } 291 | return nms; 292 | } catch (Exception e) { 293 | throw new RuntimeException(e.getMessage()); 294 | } 295 | } 296 | protected List> out(List yolo_result, Vector labels) { 297 | try { 298 | List> result = new ArrayList<>(); 299 | //utils.getScreenshotBmp(bitmap, "current"); 300 | for (float[] box : yolo_result) { 301 | Map output = new HashMap<>(); 302 | output.put("box", new float[]{box[0], box[1], box[2], box[3], box[4]}); //x1,y1,x2,y2,conf_class 303 | output.put("tag", labels.get((int) box[5])); 304 | result.add(output); 305 | } 306 | return result; 307 | } catch (Exception e) { 308 | throw e; 309 | } 310 | } 311 | 312 | public void close() { 313 | try { 314 | if (interpreter != null) 315 | interpreter.close(); 316 | } catch (Exception e) { 317 | throw e; 318 | } 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolov5.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.models; 2 | 3 | import static java.lang.Math.min; 4 | 5 | import android.content.Context; 6 | import android.content.res.AssetFileDescriptor; 7 | import android.content.res.AssetManager; 8 | import android.util.Log; 9 | 10 | import org.tensorflow.lite.Interpreter; 11 | import org.tensorflow.lite.gpu.CompatibilityList; 12 | import org.tensorflow.lite.gpu.GpuDelegate; 13 | 14 | import java.io.File; 15 | import java.io.FileInputStream; 16 | import java.nio.MappedByteBuffer; 17 | import java.nio.channels.FileChannel; 18 | import java.util.ArrayList; 19 | import java.util.Collections; 20 | import java.util.Comparator; 21 | import java.util.List; 22 | 23 | public class Yolov5 extends Yolo{ 24 | public Yolov5(Context context, 25 | String model_path, 26 | boolean is_assets, 27 | int num_threads, 28 | boolean quantization, 29 | boolean use_gpu, 30 | String label_path, 31 | int rotation) { 32 | super(context, model_path, is_assets, num_threads, quantization, use_gpu, label_path, rotation); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolov8.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.models; 2 | 3 | import static java.lang.Math.min; 4 | 5 | import android.content.Context; 6 | import android.content.res.AssetFileDescriptor; 7 | import android.content.res.AssetManager; 8 | import android.graphics.Bitmap; 9 | import android.graphics.Color; 10 | import android.util.Log; 11 | 12 | import com.vladih.computer_vision.flutter_vision.utils.utils; 13 | 14 | import org.opencv.core.Core; 15 | import org.opencv.core.CvType; 16 | import org.opencv.core.Mat; 17 | import org.opencv.core.MatOfPoint; 18 | import org.opencv.core.Point; 19 | import org.opencv.imgproc.Imgproc; 20 | import org.tensorflow.lite.Interpreter; 21 | import org.tensorflow.lite.gpu.CompatibilityList; 22 | import org.tensorflow.lite.gpu.GpuDelegate; 23 | import org.tensorflow.lite.schema.Buffer; 24 | import org.tensorflow.lite.schema.ReshapeOptions; 25 | import org.tensorflow.lite.support.image.ImageProcessor; 26 | 27 | import java.io.File; 28 | import java.io.FileInputStream; 29 | import java.lang.reflect.Array; 30 | import java.nio.ByteBuffer; 31 | import java.nio.MappedByteBuffer; 32 | import java.nio.channels.FileChannel; 33 | import java.util.ArrayList; 34 | import java.util.Collections; 35 | import java.util.Comparator; 36 | import java.util.HashMap; 37 | import java.util.List; 38 | import java.util.Map; 39 | import java.util.UUID; 40 | import java.util.Vector; 41 | 42 | public class Yolov8 extends Yolo { 43 | public Yolov8(Context context, 44 | String model_path, 45 | boolean is_assets, 46 | int num_threads, 47 | boolean quantization, 48 | boolean use_gpu, 49 | String label_path, 50 | int rotation) { 51 | super(context, model_path, is_assets, num_threads, quantization, use_gpu, label_path, rotation); 52 | } 53 | 54 | @Override 55 | public List> detect_task(ByteBuffer byteBuffer, 56 | int source_height, 57 | int source_width, 58 | float iou_threshold, 59 | float conf_threshold, 60 | float class_threshold) throws Exception { 61 | try { 62 | int[] input_shape = this.interpreter.getInputTensor(0).shape(); 63 | this.interpreter.run(byteBuffer, this.output); 64 | //INFO: output from detection model is not normalized 65 | List boxes = filter_box(this.output, iou_threshold, conf_threshold, 66 | class_threshold, input_shape[1], input_shape[2]); 67 | boxes = restore_size(boxes, input_shape[1], input_shape[2], source_width, source_height); 68 | return out(boxes, this.labels); 69 | } catch (Exception e) { 70 | throw e; 71 | } finally { 72 | byteBuffer.clear(); 73 | } 74 | } 75 | 76 | @Override 77 | protected List filter_box(float[][][] model_outputs, float iou_threshold, 78 | float conf_threshold, float class_threshold, float input_width, float input_height) { 79 | try { 80 | //model_outputs = [1,box+class,detected_box] 81 | List pre_box = new ArrayList<>(); 82 | int class_index = 4; 83 | int dimension = model_outputs[0][0].length; 84 | int rows = model_outputs[0].length; 85 | int max_index = 0; 86 | float max = 0f; 87 | for (int i = 0; i < dimension; i++) { 88 | float x1 = (model_outputs[0][0][i] - model_outputs[0][2][i] / 2f)* input_width; 89 | float y1 = (model_outputs[0][1][i] - model_outputs[0][3][i] / 2f)* input_height; 90 | float x2 = (model_outputs[0][0][i] + model_outputs[0][2][i] / 2f)* input_width; 91 | float y2 = (model_outputs[0][1][i] + model_outputs[0][3][i] / 2f)* input_height; 92 | 93 | max_index = class_index; 94 | max = model_outputs[0][max_index][i]; 95 | 96 | for (int j = class_index + 1; j < rows; j++) { 97 | float current = model_outputs[0][j][i]; 98 | if (current > max) { 99 | max = current; 100 | max_index = j; 101 | } 102 | } 103 | 104 | if (max > class_threshold) { 105 | float[] tmp = new float[6]; 106 | tmp[0] = x1; 107 | tmp[1] = y1; 108 | tmp[2] = x2; 109 | tmp[3] = y2; 110 | tmp[4] = max; 111 | tmp[5] = (max_index - class_index) * 1f; 112 | pre_box.add(tmp); 113 | } 114 | } 115 | if (pre_box.isEmpty()) return new ArrayList<>(); 116 | //for reverse orden, insteand of using .reversed method 117 | Comparator compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]); 118 | //Collections.sort(pre_box,compareValues.reversed()); 119 | Collections.sort(pre_box, compareValues); 120 | return nms(pre_box, iou_threshold); 121 | } catch (Exception e) { 122 | throw e; 123 | } 124 | } 125 | 126 | @Override 127 | protected List> out(List yolo_result, Vector labels) { 128 | try { 129 | List> result = new ArrayList<>(); 130 | for (float[] box : yolo_result) { 131 | Map output = new HashMap<>(); 132 | output.put("box", new float[]{box[0], box[1], box[2], box[3], box[4]}); //x1,y1,x2,y2,conf_class 133 | output.put("tag", labels.get((int) box[5])); 134 | result.add(output); 135 | } 136 | return result; 137 | } catch (Exception e) { 138 | throw e; 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/models/Yolov8Seg.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.models; 2 | 3 | import static java.lang.Math.min; 4 | 5 | import android.content.Context; 6 | import android.content.res.AssetFileDescriptor; 7 | import android.content.res.AssetManager; 8 | import android.graphics.Bitmap; 9 | import android.graphics.Color; 10 | import android.util.Log; 11 | 12 | import com.vladih.computer_vision.flutter_vision.utils.utils; 13 | 14 | import org.opencv.core.Core; 15 | import org.opencv.core.CvType; 16 | import org.opencv.core.Mat; 17 | import org.opencv.core.MatOfPoint; 18 | import org.opencv.core.Point; 19 | import org.opencv.imgproc.Imgproc; 20 | import org.tensorflow.lite.Interpreter; 21 | import org.tensorflow.lite.gpu.CompatibilityList; 22 | import org.tensorflow.lite.gpu.GpuDelegate; 23 | import org.tensorflow.lite.schema.Buffer; 24 | import org.tensorflow.lite.schema.ReshapeOptions; 25 | import org.tensorflow.lite.support.image.ImageProcessor; 26 | 27 | import java.io.File; 28 | import java.io.FileInputStream; 29 | import java.lang.reflect.Array; 30 | import java.nio.ByteBuffer; 31 | import java.nio.MappedByteBuffer; 32 | import java.nio.channels.FileChannel; 33 | import java.util.ArrayList; 34 | import java.util.Collections; 35 | import java.util.Comparator; 36 | import java.util.HashMap; 37 | import java.util.List; 38 | import java.util.Map; 39 | import java.util.UUID; 40 | import java.util.Vector; 41 | 42 | //https://dev.to/andreygermanov/how-to-implement-instance-segmentation-using-yolov8-neural-network-3if9 43 | //PAPER: https://openaccess.thecvf.com/content_ICCV_2019/papers/Bolya_YOLACT_Real-Time_Instance_Segmentation_ICCV_2019_paper.pdf 44 | public class Yolov8Seg extends Yolo { 45 | public Yolov8Seg(Context context, 46 | String model_path, 47 | boolean is_assets, 48 | int num_threads, 49 | boolean quantization, 50 | boolean use_gpu, 51 | String label_path, 52 | int rotation) { 53 | super(context, model_path, is_assets, num_threads, quantization, use_gpu, label_path, rotation); 54 | } 55 | 56 | @Override 57 | public List> detect_task(ByteBuffer byteBuffer, 58 | int source_height, 59 | int source_width, 60 | float iou_threshold, 61 | float conf_threshold, 62 | float class_threshold) { 63 | try { 64 | if (has_multiple_output()) { 65 | Map outputs = new HashMap<>(); 66 | for (int i = 0; i < interpreter.getOutputTensorCount(); i++) { 67 | int[] shape = interpreter.getOutputTensor(i).shape(); 68 | outputs.put(i, Array.newInstance(float.class, shape)); 69 | } 70 | Object[] inputs = {byteBuffer}; 71 | this.interpreter.runForMultipleInputsOutputs(inputs, outputs); 72 | 73 | int[] input_shape = interpreter.getInputTensor(0).shape(); // 1, 640, 640 74 | int[] output0_shape = interpreter.getOutputTensor(0).shape(); //1,116,2184 75 | int[] output1_shape = interpreter.getOutputTensor(1).shape(); //1,160,160,32 76 | 77 | float[][][] output0 = (float[][][]) outputs.get(0); 78 | 79 | //seg_boxes = coordinates[4]+classes[x=84]+masks_weight[32] 80 | //INFO: output from segment model return normalized values 81 | List seg_boxes = filter_box(output0, 82 | iou_threshold, conf_threshold, class_threshold, 83 | input_shape[1], input_shape[2]); 84 | 85 | output0 = null; 86 | 87 | //it only restores the size of the boxes, nothing has been done with mask_weight 88 | seg_boxes = restore_size(seg_boxes, input_shape[1], input_shape[2], 89 | source_width, source_height); 90 | 91 | float[][][][] masks = (float[][][][]) outputs.get(1); 92 | List seg_boxes_mask = new ArrayList<>(); 93 | for (float[] mask_weight : seg_boxes) { 94 | seg_boxes_mask.add(compute_mask(mask_weight, 95 | masks[0], (float) 0.3, 96 | output1_shape[1], output1_shape[2])); 97 | } 98 | masks = null; 99 | List>> restore_seg_mask = restore_seg_mask_size(seg_boxes, 100 | seg_boxes_mask, output1_shape[1], output1_shape[2], source_height, source_width 101 | ); 102 | return out_segmentation(seg_boxes, restore_seg_mask, this.labels); 103 | } else { 104 | throw new ExceptionInInitializerError("tflite model should have two outputs in segmentation mode"); 105 | } 106 | } catch (Exception e) { 107 | throw e; 108 | } finally { 109 | byteBuffer.clear(); 110 | } 111 | } 112 | 113 | private int[] compute_mask(float[] mask_weight, 114 | float[][][] masks_protos, 115 | float seg_thresh, 116 | int mask_height, 117 | int mask_width) { 118 | int prefix_box = 6; 119 | int numMask = mask_weight.length - prefix_box; 120 | int[] masks = new int[mask_height * mask_width]; 121 | int index = 0; 122 | // Set all pixels to either white (255) or black (0) 123 | for (int h = 0; h < mask_height; h++) { 124 | for (int w = 0; w < mask_width; w++) { 125 | float sum = 0.0f; 126 | for (int j = 0; j < numMask; j++) { 127 | sum += mask_weight[j + prefix_box] * masks_protos[h][w][j]; 128 | } 129 | if (sigmoid(sum) > seg_thresh) { 130 | masks[index++] = Color.WHITE; 131 | } else { 132 | masks[index++] = Color.BLACK; 133 | } 134 | } 135 | // System.out.println(); 136 | } 137 | // Bitmap bitmap =Bitmap.createBitmap(masks, maskHeight, maskWidth, Bitmap.Config.ARGB_8888); 138 | // utils.getScreenshotBmp(bitmap, UUID.randomUUID().toString()); 139 | return masks; 140 | } 141 | 142 | float sigmoid(float x) { 143 | return (float) (1.0 / (1.0 + Math.exp(-x))); 144 | } 145 | 146 | private List>> restore_seg_mask_size(List boxes, List seg_mask, 147 | int mask_height, int mask_width, 148 | int source_height, int source_width) { 149 | Bitmap bitmap = null; 150 | Bitmap crop = null; 151 | try { 152 | List>> polygons = new ArrayList<>(); 153 | for (int i = 0; i < boxes.size(); i++) { 154 | // Set the pixel data from the flattened array 155 | bitmap = Bitmap.createBitmap(seg_mask.get(i), mask_width, mask_height, Bitmap.Config.ARGB_8888); 156 | // String tag = UUID.randomUUID().toString(); 157 | // utils.getScreenshotBmp(bitmap, tag+"0"); 158 | crop = utils.crop_bitmap(bitmap, 159 | min(mask_width, Math.max(boxes.get(i)[0] * mask_width / source_width, 0)), 160 | min(mask_height, Math.max(boxes.get(i)[1] * mask_height / source_height, 0)), 161 | min(mask_width, Math.max(boxes.get(i)[2] * mask_width / source_width, 0)), 162 | min(mask_height, Math.max(boxes.get(i)[3] * mask_height / source_height, 0)) 163 | ); 164 | // utils.getScreenshotBmp(crop, tag+"1"); 165 | List> crop_polygon = get_polygons_from_bitmap(crop, mask_height, 166 | mask_width, source_height, source_width); 167 | polygons.add(crop_polygon); 168 | } 169 | return polygons; 170 | } catch (Exception e) { 171 | throw e; 172 | } finally { 173 | if (bitmap != null) bitmap.recycle(); 174 | if (crop != null) crop.recycle(); 175 | } 176 | } 177 | 178 | public static List> get_polygons_from_bitmap(Bitmap mask, 179 | int mask_height, 180 | int mask_width, 181 | int source_height, 182 | int source_width) { 183 | Mat maskMat = utils.rgbBitmapToMatGray(mask); // Convert Bitmap to Mat 184 | List contours = new ArrayList<>(); 185 | Imgproc.findContours(maskMat, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); 186 | 187 | MatOfPoint largestContour = null; 188 | double largestArea = 0; 189 | 190 | for (MatOfPoint contour : contours) { 191 | double area = Imgproc.contourArea(contour); 192 | if (area > largestArea) { 193 | largestArea = area; 194 | largestContour = contour; 195 | } 196 | } 197 | List polygon = new ArrayList<>(largestContour.toList()); 198 | // List> polygons = new ArrayList<>(); 199 | // for (MatOfPoint contour : contours) { 200 | // List polygon = new ArrayList<>(); 201 | // for (Point point : contour.toList()) { 202 | // polygon.add(point); 203 | // } 204 | // polygons.add(polygon); 205 | // } 206 | // List>> converted_polygons = new ArrayList<>(); 207 | 208 | // for (List polygon : polygons) { 209 | List> convertedPolygon = new ArrayList<>(); 210 | for (Point point : polygon) { 211 | Map pointMap = new HashMap<>(); 212 | pointMap.put("x", point.x * source_width / mask_width); 213 | pointMap.put("y", point.y * source_height / mask_height); 214 | convertedPolygon.add(pointMap); 215 | } 216 | // converted_polygons.add(convertedPolygon); 217 | // } 218 | // return converted_polygons; 219 | return convertedPolygon; 220 | } 221 | 222 | private boolean has_multiple_output() { 223 | return this.interpreter.getOutputTensorCount() > 1; 224 | } 225 | 226 | @Override 227 | protected List filter_box(float[][][] model_outputs, float iou_threshold, 228 | float conf_threshold, float class_threshold, 229 | float input_width, float input_height) { 230 | try { 231 | //model_outputs = [1,box+class+mask_weight,detected_box] 232 | List pre_box = new ArrayList<>(); 233 | int class_index = 4; 234 | int dimension = model_outputs[0][0].length; 235 | int rows = model_outputs[0].length; 236 | int index_mask = rows - 32; 237 | float[] mask_weight = new float[32]; 238 | int max_index = 0; 239 | float max = 0f; 240 | for (int i = 0; i < dimension; i++) { 241 | // Convertir xywh a xyxy y ajustar por el ancho y alto de entrada 242 | float x1 = (model_outputs[0][0][i] - model_outputs[0][2][i] / 2f) * input_width; 243 | float y1 = (model_outputs[0][1][i] - model_outputs[0][3][i] / 2f) * input_height; 244 | float x2 = (model_outputs[0][0][i] + model_outputs[0][2][i] / 2f) * input_width; 245 | float y2 = (model_outputs[0][1][i] + model_outputs[0][3][i] / 2f) * input_height; 246 | 247 | max_index = class_index; 248 | max = model_outputs[0][max_index][i]; 249 | 250 | for (int j = class_index + 1; j < index_mask; j++) { 251 | float current = model_outputs[0][j][i]; 252 | if (current > max) { 253 | max = current; 254 | max_index = j; 255 | } 256 | } 257 | 258 | if (max > class_threshold) { 259 | float[] tmp = new float[38]; 260 | tmp[0] = x1; 261 | tmp[1] = y1; 262 | tmp[2] = x2; 263 | tmp[3] = y2; 264 | tmp[4] = max; 265 | tmp[5] = (max_index - class_index) * 1f; 266 | for (int j = index_mask; j < rows; j++) { 267 | tmp[j - index_mask + 6] = model_outputs[0][j][i]; 268 | } 269 | pre_box.add(tmp); 270 | } 271 | } 272 | if (pre_box.isEmpty()) return new ArrayList<>(); 273 | //for reverse orden, insteand of using .reversed method 274 | Comparator compareValues = (v1, v2) -> Float.compare(v2[4], v1[4]); 275 | //Collections.sort(pre_box,compareValues.reversed()); 276 | Collections.sort(pre_box, compareValues); 277 | return nms(pre_box, iou_threshold); 278 | // return nms_segmentation(pre_box, iou_threshold); 279 | } catch (Exception e) { 280 | throw e; 281 | } 282 | } 283 | 284 | //ignore out method of super class 285 | protected List> out_segmentation(List yolo_result, 286 | List>> polygons, 287 | Vector labels) { 288 | try { 289 | List> result = new ArrayList<>(); 290 | for (int i = 0; i < yolo_result.size(); i++) { 291 | Map output = new HashMap<>(); 292 | output.put("box", new float[]{yolo_result.get(i)[0], yolo_result.get(i)[1], 293 | yolo_result.get(i)[2], yolo_result.get(i)[3], yolo_result.get(i)[4]}); //x1,y1,x2,y2,conf_class 294 | output.put("polygons", polygons.get(i)); 295 | output.put("tag", labels.get((int) yolo_result.get(i)[5])); 296 | result.add(output); 297 | } 298 | return result; 299 | } catch (Exception e) { 300 | throw e; 301 | } 302 | } 303 | } 304 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/utils/FeedInputTensorHelper.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.utils; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | import com.googlecode.leptonica.android.Scale; 6 | 7 | import org.tensorflow.lite.DataType; 8 | import org.tensorflow.lite.support.common.ops.NormalizeOp; 9 | import org.tensorflow.lite.support.image.ImageProcessor; 10 | import org.tensorflow.lite.support.image.TensorImage; 11 | import org.tensorflow.lite.support.image.ops.ResizeOp; 12 | import org.tensorflow.lite.support.image.ops.ResizeWithCropOrPadOp; 13 | 14 | public class FeedInputTensorHelper { 15 | private static FeedInputTensorHelper instance; 16 | private TensorImage tensorImage; 17 | private ImageProcessor downSizeImageProcessor; 18 | private ImageProcessor upSizeImageProcessor; 19 | 20 | private int previus_width = 0; 21 | private int previus_height = 0; 22 | private FeedInputTensorHelper(int width, int height, float mean, float std) { 23 | previus_width = width; 24 | previus_height = height; 25 | tensorImage = new TensorImage(DataType.FLOAT32); 26 | downSizeImageProcessor = 27 | new ImageProcessor.Builder() 28 | // Resize using Bilinear or Nearest neighbour 29 | .add(new ResizeOp(height, width, ResizeOp.ResizeMethod.BILINEAR)) 30 | // Rotation counter-clockwise in 90 degree increments 31 | // .add(new Rot90Op(rotateDegrees / 90)) 32 | .add(new NormalizeOp(mean, std)) 33 | // .add(new QuantizeOp(128.0, 1/128.0)) 34 | .build(); 35 | upSizeImageProcessor = 36 | new ImageProcessor.Builder() 37 | // Center crop the image to the largest square possible 38 | .add(new ResizeWithCropOrPadOp(height, width)) 39 | .add(new NormalizeOp(mean, std)) 40 | .build(); 41 | } 42 | 43 | public static synchronized FeedInputTensorHelper getInstance(int width, int height, float mean, float std) { 44 | if (instance == null) { 45 | instance = new FeedInputTensorHelper(width, height,mean, std); 46 | }else{ 47 | if (instance.previus_width!=width || instance.previus_height!=height){ 48 | instance = new FeedInputTensorHelper(width, height,mean, std); 49 | } 50 | } 51 | return instance; 52 | } 53 | 54 | public static TensorImage getBytebufferFromBitmap(Bitmap bitmap, 55 | int input_width, 56 | int input_height, float mean, float std, String size_option) throws Exception { 57 | try{ 58 | //https://www.tensorflow.org/lite/inference_with_metadata/lite_support 59 | FeedInputTensorHelper feedInputTensorHelper = getInstance(input_width, input_height, mean, std); 60 | feedInputTensorHelper.tensorImage.load(bitmap); 61 | if (size_option=="downsize"){ 62 | return feedInputTensorHelper.downSizeImageProcessor.process(feedInputTensorHelper.tensorImage); 63 | } 64 | if (size_option=="upsize"){ 65 | return feedInputTensorHelper.upSizeImageProcessor.process(feedInputTensorHelper.tensorImage); 66 | } 67 | throw new Exception("internal error, size_option no supported"); 68 | }catch (Exception e){ 69 | throw e; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/utils/RenderScriptHelper.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.utils; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.renderscript.Allocation; 7 | import android.renderscript.Element; 8 | import android.renderscript.RenderScript; 9 | import android.renderscript.ScriptIntrinsicYuvToRGB; 10 | import android.renderscript.Type; 11 | 12 | public class RenderScriptHelper { 13 | private static RenderScriptHelper instance; 14 | 15 | private RenderScript rs; 16 | private ScriptIntrinsicYuvToRGB yuvToRgbIntrinsic; 17 | private Type.Builder yuvType; 18 | private Type.Builder rgbaType; 19 | private Allocation in; 20 | private Allocation out; 21 | 22 | private RenderScriptHelper(Context context) { 23 | rs = RenderScript.create(context); 24 | yuvToRgbIntrinsic = ScriptIntrinsicYuvToRGB.create(rs, Element.U8_4(rs)); 25 | } 26 | 27 | public static synchronized RenderScriptHelper getInstance(Context context) { 28 | if (instance == null) { 29 | instance = new RenderScriptHelper(context); 30 | } 31 | return instance; 32 | } 33 | 34 | public Allocation renderScriptNV21ToRGBA888(int width, int height, byte[] nv21) { 35 | if (yuvType == null) { 36 | yuvType = new Type.Builder(rs, Element.U8(rs)).setX(nv21.length); 37 | } 38 | if (rgbaType == null) { 39 | rgbaType = new Type.Builder(rs, Element.RGBA_8888(rs)).setX(width).setY(height); 40 | } 41 | // Create input allocation for YUV data 42 | if (in == null) { 43 | in = Allocation.createTyped(rs, yuvType.create(), Allocation.USAGE_SCRIPT); 44 | } 45 | // Create output allocation for RGBA data 46 | if (out == null) { 47 | out = Allocation.createTyped(rs, rgbaType.create(), Allocation.USAGE_SCRIPT); 48 | } 49 | 50 | // Convert YUV to RGBA using RenderScript intrinsic 51 | in.copyFrom(nv21); 52 | yuvToRgbIntrinsic.setInput(in); 53 | yuvToRgbIntrinsic.forEach(out); 54 | return out; 55 | } 56 | 57 | public static Bitmap getBitmapFromNV21(Context context, byte[] nv21, int width, int height) { 58 | RenderScriptHelper rsHelper = getInstance(context); 59 | //https://blog.minhazav.dev/how-to-convert-yuv-420-sp-android.media.Image-to-Bitmap-or-jpeg/ 60 | Allocation allocation = rsHelper.renderScriptNV21ToRGBA888(width, height, nv21); 61 | 62 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 63 | allocation.copyTo(bitmap); 64 | 65 | return bitmap; 66 | } 67 | } -------------------------------------------------------------------------------- /android/src/main/java/com/vladih/computer_vision/flutter_vision/utils/utils.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision.utils; 2 | import static java.lang.Math.min; 3 | 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.Matrix; 7 | import android.os.Environment; 8 | 9 | import org.opencv.android.Utils; 10 | import org.opencv.core.Core; 11 | import org.opencv.core.CvType; 12 | import org.opencv.core.Mat; 13 | import org.opencv.core.MatOfFloat; 14 | import org.opencv.core.MatOfPoint; 15 | import org.opencv.core.Point; 16 | import org.opencv.core.Rect; 17 | import org.opencv.core.Scalar; 18 | import org.opencv.core.Size; 19 | import org.opencv.imgproc.Imgproc; 20 | import org.opencv.photo.Photo; 21 | import org.tensorflow.lite.support.image.TensorImage; 22 | 23 | import java.io.ByteArrayOutputStream; 24 | import java.io.File; 25 | import java.io.FileNotFoundException; 26 | import java.io.FileOutputStream; 27 | import java.io.IOException; 28 | import java.nio.ByteBuffer; 29 | import java.util.ArrayList; 30 | import java.util.Collections; 31 | import java.util.Comparator; 32 | import java.util.List; 33 | 34 | public class utils { 35 | public static Bitmap crop_bitmap(Bitmap bitmap, float x1, float y1, float x2, float y2) { 36 | try{ 37 | final int x = Math.max((int)x1,0); 38 | final int y = Math.max((int)y1,0); 39 | final int width = Math.abs((int)(x2-x1)); 40 | final int height = Math.abs((int)(y2-y1)); 41 | return Bitmap.createBitmap(bitmap,x,y,width,height); 42 | }catch (Exception e){ 43 | throw e; 44 | } 45 | } 46 | public static byte[] bitmap_to_byte(Bitmap bitmap){ 47 | ByteArrayOutputStream stream = new ByteArrayOutputStream(); 48 | bitmap.compress(Bitmap.CompressFormat.JPEG,100,stream); 49 | return stream.toByteArray(); 50 | } 51 | public static Bitmap getScreenshotBmp(Bitmap bitmap, String name) { 52 | FileOutputStream fileOutputStream = null; 53 | 54 | File path = Environment 55 | .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); 56 | 57 | String uniqueID = name; 58 | 59 | File file = new File(path, uniqueID + ".jpg"); 60 | try { 61 | fileOutputStream = new FileOutputStream(file); 62 | } catch (FileNotFoundException e) { 63 | e.printStackTrace(); 64 | } 65 | 66 | bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream); 67 | 68 | try { 69 | fileOutputStream.flush(); 70 | fileOutputStream.close(); 71 | } catch (IOException e) { 72 | e.printStackTrace(); 73 | } 74 | return bitmap; 75 | } 76 | //These code lines work well but is so slower, now rename as filterTextFromImage 77 | // public static Mat image_preprocessing(Mat mat){ 78 | // try { 79 | // Photo.fastNlMeansDenoising(mat,mat, new MatOfFloat(7),3,21, Core.NORM_L1); 80 | // Core.normalize(mat,mat, 0, 255, Core.NORM_MINMAX); 81 | // Imgproc.GaussianBlur(mat, mat, new Size(5,5), 1); 82 | // Imgproc.adaptiveThreshold(mat,mat,255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY,21,15); 83 | // return mat; 84 | // }catch (Exception e){ 85 | // throw e; 86 | // } 87 | // } 88 | //Accept gray Mat 89 | public static Mat filterTextFromImage(Mat mat){ 90 | try { 91 | //find posibles box text 92 | List rects = findRects(mat); 93 | //join posibles box text that belong to same horizontal line 94 | rects = mergeRects(rects); 95 | //remove boxes which belong to another 96 | rects = non_max_suppression(rects); 97 | 98 | Mat new_image = new Mat(mat.height(), mat.width(), CvType.CV_8UC1, new Scalar(255)); 99 | for (Rect box : rects) { 100 | try{ 101 | //Todo: Still there are error to fix when image have black border ie. images, stains 102 | //this errors are produced by findRects function, also text doesnt working when 103 | // images has wave text 104 | Mat crop = mat.submat(box); 105 | Core.normalize(crop, crop, 0, 255, Core.NORM_MINMAX); 106 | Imgproc.threshold(crop, crop, 0, 255, Imgproc.THRESH_BINARY + Imgproc.THRESH_OTSU); 107 | Scalar meanScalar = Core.mean(crop); 108 | double meanValue = meanScalar.val[0]; 109 | if (meanValue<100){ 110 | continue; 111 | } 112 | Mat roi = new_image.submat(new Rect(box.x, box.y, box.width, box.height)); 113 | Core.bitwise_and(crop,roi,roi); 114 | }catch (Exception e){ 115 | System.err.println("Warning, vission text error filter"); 116 | } 117 | // crop.copyTo(roi); 118 | } 119 | return new_image; 120 | }catch (Exception e){ 121 | throw e; 122 | } 123 | } 124 | public static Mat rgbBitmapToMatGray(Bitmap bitmap){ 125 | Mat mat = new Mat(bitmap.getHeight(), bitmap.getWidth(), CvType.CV_8UC3); 126 | Utils.bitmapToMat(bitmap,mat); 127 | Imgproc.cvtColor(mat,mat, Imgproc.COLOR_RGB2GRAY); 128 | return mat; 129 | } 130 | 131 | public static List non_max_suppression(List boxes) { 132 | // Sort the list of bounding boxes in ascending order based on their y-coordinates 133 | Collections.sort(boxes, new Comparator() { 134 | @Override 135 | public int compare(Rect r1, Rect r2) { 136 | return Integer.compare(r1.y, r2.y); 137 | } 138 | }); 139 | 140 | // Initialize the list of selected boxes 141 | List selected_boxes = new ArrayList<>(); 142 | 143 | // Perform NMS 144 | while (boxes.size() > 0) { 145 | Rect current = boxes.get(0); 146 | selected_boxes.add(current); 147 | boxes.remove(0); 148 | 149 | List next_boxes = new ArrayList<>(); 150 | for (Rect box : boxes) { 151 | if (!contain(current, box) && (box.width/ box.height)>1) { 152 | next_boxes.add(box); 153 | } 154 | } 155 | boxes = next_boxes; 156 | } 157 | 158 | return selected_boxes; 159 | } 160 | 161 | public static boolean contain(Rect box1, Rect box2) { 162 | // Calculate the coordinates of the intersection rectangle 163 | int x1 = Math.max(box1.x, box2.x); 164 | int y1 = Math.max(box1.y, box2.y); 165 | int x2 = Math.min(box1.x+box1.width, box2.x+box2.width); 166 | int y2 = Math.min(box1.y+box1.height, box2.y+box2.height); 167 | int w = Math.max(0, x2 - x1); 168 | int h = Math.max(0, y2 - y1); 169 | 170 | // Calculate the area of intersection rectangle 171 | int intersection = w * h; 172 | 173 | // Calculate the area of both bounding boxes 174 | // int area_box1 = box1.width * box1.height; 175 | int area_box2 = box2.width * box2.height; 176 | 177 | return area_box2 == intersection; 178 | } 179 | public static List mergeRects(List rects){ 180 | Collections.sort(rects, new Comparator() { 181 | @Override 182 | public int compare(Rect r1, Rect r2) { 183 | return r1.y - r2.y; 184 | } 185 | }); 186 | List mergedBoxes = new ArrayList<>(); 187 | for (int i = 0; i < rects.size(); i++) { 188 | Rect rect = rects.get(i); 189 | if (mergedBoxes.isEmpty()) { 190 | mergedBoxes.add(rect); 191 | } else { 192 | Rect prevRect = mergedBoxes.get(mergedBoxes.size()-1); 193 | if (Math.abs(rect.y - prevRect.y) > prevRect.height * 0.5) { 194 | mergedBoxes.add(rect); 195 | } else { 196 | if(rect.x - (prevRect.x + prevRect.width)> 0){ 197 | mergedBoxes.add(rect); 198 | } 199 | else{ 200 | int newX = Math.min(rect.x, prevRect.x); 201 | int newY = Math.min(rect.y, prevRect.y); 202 | int newW = Math.max(rect.x + rect.width, prevRect.x + prevRect.width) - newX; 203 | int newH = Math.max(rect.y + rect.height, prevRect.y + prevRect.height) - newY; 204 | mergedBoxes.set(mergedBoxes.size()-1, new Rect(newX, newY, newW, newH)); 205 | } 206 | } 207 | } 208 | } 209 | return mergedBoxes; 210 | } 211 | public static List findRects(Mat image){ 212 | Mat thresh = new Mat(); 213 | //find countours in gray image 214 | Imgproc.Canny(image, thresh, 50, 150, 3, false); 215 | List contours = new ArrayList<>(); 216 | Mat hierarchy = new Mat(); 217 | Imgproc.findContours(thresh, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE); 218 | List filteredContours = new ArrayList<>(); 219 | double height = 0; 220 | for (int i = 0; i < contours.size(); i++) { 221 | Rect rect = Imgproc.boundingRect(contours.get(i)); 222 | double area = rect.width * rect.height; 223 | double aspectRatio = (double)rect.width / rect.height; 224 | //remove rect that doesn't satisface this rules 225 | if (area > 80 && aspectRatio > 0.4 && aspectRatio < 5) { 226 | height += rect.height; 227 | filteredContours.add(new Rect((int)Math.max(0, rect.x-rect.height/2), rect.y, (int)Math.min(image.width(),rect.width+rect.height), rect.height)); 228 | } 229 | } 230 | //remove rects with large height than mean height 231 | height = height / Math.sqrt(filteredContours.size()); 232 | List rects = new ArrayList<>(); 233 | for (int i = 0; i < filteredContours.size(); i++) { 234 | Rect rect = filteredContours.get(i); 235 | if (rect.height > height) continue; 236 | rects.add(rect); 237 | } 238 | return rects; 239 | } 240 | public static Mat deskew(Mat image, double skewAngle) { 241 | try { 242 | Mat rotationMatrix = Imgproc.getRotationMatrix2D(new Point(image.width() / 2, image.height() / 2), skewAngle, 1); 243 | Scalar borderValue = new Scalar(255); // white border value 244 | // Crop the output image to remove border artifacts 245 | Rect cropRect = new Rect(0, 0, image.width(), image.height()); 246 | // System.out.println(image.size()); 247 | // System.out.println(skewAngle); 248 | Imgproc.warpAffine(image, image, rotationMatrix, image.size(), Imgproc.INTER_CUBIC + Imgproc.WARP_FILL_OUTLIERS, Core.BORDER_CONSTANT, borderValue); 249 | image = image.submat(cropRect); 250 | return image; 251 | }catch (Exception e){ 252 | throw e; 253 | } 254 | } 255 | 256 | //input:binary matrix 257 | public static double computeSkewAngle(Mat image) { 258 | try { 259 | // Apply Canny edge detection to find the edges in the image 260 | Imgproc.Canny(image, image, 50, 150, 3); 261 | // Apply the Hough transform to find the lines in the image 262 | Mat lines = new Mat(); 263 | Imgproc.HoughLinesP(image, lines, 1, Math.PI / 180, 100, 100, 10); 264 | 265 | // Compute the average angle of the lines 266 | double angle = 0.0; 267 | int numLines = lines.cols(); 268 | if (numLines > 0) { 269 | // Find the longest line 270 | double longestLineLength = -1; 271 | Point[] longestLine = null; 272 | for (int i = 0; i < lines.cols(); i++) { 273 | double[] line = lines.get(0, i); 274 | Point pt1 = new Point(line[0], line[1]); 275 | Point pt2 = new Point(line[2], line[3]); 276 | double length = Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2)); 277 | if (length > longestLineLength) { 278 | longestLineLength = length; 279 | longestLine = new Point[] { pt1, pt2 }; 280 | } 281 | } 282 | // Calculate angle between longest line and horizontal axis 283 | double dx = longestLine[1].x - longestLine[0].x; 284 | double dy = longestLine[1].y - longestLine[0].y; 285 | angle = Math.atan2(dy, dx) * 180 / Math.PI; 286 | } 287 | // Convert the angle from radians to degrees and return it 288 | return angle; 289 | } catch (Exception e) { 290 | throw e; 291 | } 292 | } 293 | 294 | 295 | public static ByteBuffer feedInputTensor( 296 | Bitmap bitmap, 297 | int input_width, 298 | int input_height, 299 | int src_width, 300 | int src_height, 301 | float mean, 302 | float std) throws Exception { 303 | try { 304 | // utils.getScreenshotBmp(bitmap, "antes"); 305 | TensorImage tensorImage; 306 | if (src_width > input_width || src_height > input_height) { 307 | tensorImage= FeedInputTensorHelper.getBytebufferFromBitmap(bitmap, input_width, input_height, mean, std, "downsize"); 308 | }else{ 309 | tensorImage= FeedInputTensorHelper.getBytebufferFromBitmap(bitmap, input_width, input_height, mean, std, "upsize"); 310 | } 311 | // utils.getScreenshotBmp(tensorImage.getBitmap(), "despues"); 312 | return tensorImage.getBuffer(); 313 | }catch (Exception e){ 314 | throw e; 315 | 316 | }finally { 317 | assert bitmap != null; 318 | if(!bitmap.isRecycled()){ 319 | bitmap.recycle(); 320 | } 321 | } 322 | } 323 | public static Bitmap feedInputToBitmap(Context context, 324 | List bytesList, 325 | int imageHeight, 326 | int imageWidth, 327 | int rotation) throws Exception { 328 | 329 | int Yb = bytesList.get(0).length; 330 | int Ub = bytesList.get(1).length ; 331 | int Vb = bytesList.get(2).length ; 332 | // Copy YUV data to plane byte 333 | byte[] data = new byte[Yb+Ub+Vb]; 334 | System.arraycopy(bytesList.get(0), 0, data, 0, Yb); 335 | System.arraycopy(bytesList.get(2), 0, data, Yb, Ub); 336 | System.arraycopy(bytesList.get(1), 0, data, Yb+Ub, Vb); 337 | 338 | Bitmap bitmapRaw = RenderScriptHelper.getBitmapFromNV21(context,data, imageWidth, imageHeight); 339 | // utils.getScreenshotBmp(bitmapRaw, "NV21"); 340 | Matrix matrix = new Matrix(); 341 | matrix.postRotate(rotation); 342 | bitmapRaw = Bitmap.createBitmap(bitmapRaw, 0, 0, bitmapRaw.getWidth(), bitmapRaw.getHeight(), matrix, true); 343 | return bitmapRaw; 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 7e9793dee1b85a243edd0e06cb1658e98b077561 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_vision_example 2 | 3 | Demonstrates how to use the flutter_vision plugin. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | defaultConfig { 37 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 38 | applicationId "com.vladih.computer_vision.flutter_vision_example" 39 | minSdkVersion localProperties.getProperty('flutter.minSdkVersion').toInteger() 40 | targetSdkVersion flutter.targetSdkVersion 41 | versionCode flutterVersionCode.toInteger() 42 | versionName flutterVersionName 43 | } 44 | 45 | buildTypes { 46 | release { 47 | // TODO: Add your own signing config for the release build. 48 | // Signing with the debug keys for now, so `flutter run --release` works. 49 | signingConfig signingConfigs.debug 50 | } 51 | } 52 | } 53 | 54 | flutter { 55 | source '../..' 56 | } 57 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 10 | 18 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/vladih/computer_vision/flutter_vision_example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.vladih.computer_vision.flutter_vision_example; 2 | 3 | import io.flutter.embedding.android.FlutterActivity; 4 | 5 | public class MainActivity extends FlutterActivity { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | tasks.register("clean", Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | #org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | org.gradle.jvmargs=-Xmx4096m 5 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Feb 22 16:51:52 CET 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/assets/labels.txt: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorcycle 5 | airplane 6 | bus 7 | train 8 | truck 9 | boat 10 | traffic light 11 | fire hydrant 12 | stop sign 13 | parking meter 14 | bench 15 | bird 16 | cat 17 | dog 18 | horse 19 | sheep 20 | cow 21 | elephant 22 | bear 23 | zebra 24 | giraffe 25 | backpack 26 | umbrella 27 | handbag 28 | tie 29 | suitcase 30 | frisbee 31 | skis 32 | snowboard 33 | sports ball 34 | kite 35 | baseball bat 36 | baseball glove 37 | skateboard 38 | surfboard 39 | tennis racket 40 | bottle 41 | wine glass 42 | cup 43 | fork 44 | knife 45 | spoon 46 | bowl 47 | banana 48 | apple 49 | sandwich 50 | orange 51 | broccoli 52 | carrot 53 | hot dog 54 | pizza 55 | donut 56 | cake 57 | chair 58 | couch 59 | potted plant 60 | bed 61 | dining table 62 | toilet 63 | tv 64 | laptop 65 | mouse 66 | remote 67 | keyboard 68 | cell phone 69 | microwave 70 | oven 71 | toaster 72 | sink 73 | refrigerator 74 | book 75 | clock 76 | vase 77 | scissors 78 | teddy bear 79 | hair drier 80 | toothbrush -------------------------------------------------------------------------------- /example/assets/tessdata/spa.traineddata: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/assets/tessdata/spa.traineddata -------------------------------------------------------------------------------- /example/assets/tessdata_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "spa.traineddata" 4 | ] 5 | } -------------------------------------------------------------------------------- /example/assets/yolov5n.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/assets/yolov5n.tflite -------------------------------------------------------------------------------- /example/assets/yolov8n-seg.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/assets/yolov8n-seg.tflite -------------------------------------------------------------------------------- /example/assets/yolov8n.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/assets/yolov8n.tflite -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1300; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = ( 294 | "$(inherited)", 295 | "@executable_path/Frameworks", 296 | ); 297 | PRODUCT_BUNDLE_IDENTIFIER = com.vladih.computervision.flutterVisionExample; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 300 | SWIFT_VERSION = 5.0; 301 | VERSIONING_SYSTEM = "apple-generic"; 302 | }; 303 | name = Profile; 304 | }; 305 | 97C147031CF9000F007C117D /* Debug */ = { 306 | isa = XCBuildConfiguration; 307 | buildSettings = { 308 | ALWAYS_SEARCH_USER_PATHS = NO; 309 | CLANG_ANALYZER_NONNULL = YES; 310 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 311 | CLANG_CXX_LIBRARY = "libc++"; 312 | CLANG_ENABLE_MODULES = YES; 313 | CLANG_ENABLE_OBJC_ARC = YES; 314 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 315 | CLANG_WARN_BOOL_CONVERSION = YES; 316 | CLANG_WARN_COMMA = YES; 317 | CLANG_WARN_CONSTANT_CONVERSION = YES; 318 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 319 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 320 | CLANG_WARN_EMPTY_BODY = YES; 321 | CLANG_WARN_ENUM_CONVERSION = YES; 322 | CLANG_WARN_INFINITE_RECURSION = YES; 323 | CLANG_WARN_INT_CONVERSION = YES; 324 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 326 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 327 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 328 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 329 | CLANG_WARN_STRICT_PROTOTYPES = YES; 330 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | COPY_PHASE_STRIP = NO; 335 | DEBUG_INFORMATION_FORMAT = dwarf; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | ENABLE_TESTABILITY = YES; 338 | GCC_C_LANGUAGE_STANDARD = gnu99; 339 | GCC_DYNAMIC_NO_PIC = NO; 340 | GCC_NO_COMMON_BLOCKS = YES; 341 | GCC_OPTIMIZATION_LEVEL = 0; 342 | GCC_PREPROCESSOR_DEFINITIONS = ( 343 | "DEBUG=1", 344 | "$(inherited)", 345 | ); 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 353 | MTL_ENABLE_DEBUG_INFO = YES; 354 | ONLY_ACTIVE_ARCH = YES; 355 | SDKROOT = iphoneos; 356 | TARGETED_DEVICE_FAMILY = "1,2"; 357 | }; 358 | name = Debug; 359 | }; 360 | 97C147041CF9000F007C117D /* Release */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | ALWAYS_SEARCH_USER_PATHS = NO; 364 | CLANG_ANALYZER_NONNULL = YES; 365 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 366 | CLANG_CXX_LIBRARY = "libc++"; 367 | CLANG_ENABLE_MODULES = YES; 368 | CLANG_ENABLE_OBJC_ARC = YES; 369 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 370 | CLANG_WARN_BOOL_CONVERSION = YES; 371 | CLANG_WARN_COMMA = YES; 372 | CLANG_WARN_CONSTANT_CONVERSION = YES; 373 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 374 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 375 | CLANG_WARN_EMPTY_BODY = YES; 376 | CLANG_WARN_ENUM_CONVERSION = YES; 377 | CLANG_WARN_INFINITE_RECURSION = YES; 378 | CLANG_WARN_INT_CONVERSION = YES; 379 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 380 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 381 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 382 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 383 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 384 | CLANG_WARN_STRICT_PROTOTYPES = YES; 385 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 386 | CLANG_WARN_UNREACHABLE_CODE = YES; 387 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 388 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 389 | COPY_PHASE_STRIP = NO; 390 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 391 | ENABLE_NS_ASSERTIONS = NO; 392 | ENABLE_STRICT_OBJC_MSGSEND = YES; 393 | GCC_C_LANGUAGE_STANDARD = gnu99; 394 | GCC_NO_COMMON_BLOCKS = YES; 395 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 396 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 397 | GCC_WARN_UNDECLARED_SELECTOR = YES; 398 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 399 | GCC_WARN_UNUSED_FUNCTION = YES; 400 | GCC_WARN_UNUSED_VARIABLE = YES; 401 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 402 | MTL_ENABLE_DEBUG_INFO = NO; 403 | SDKROOT = iphoneos; 404 | SUPPORTED_PLATFORMS = iphoneos; 405 | SWIFT_COMPILATION_MODE = wholemodule; 406 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 407 | TARGETED_DEVICE_FAMILY = "1,2"; 408 | VALIDATE_PRODUCT = YES; 409 | }; 410 | name = Release; 411 | }; 412 | 97C147061CF9000F007C117D /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 415 | buildSettings = { 416 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 417 | CLANG_ENABLE_MODULES = YES; 418 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 419 | ENABLE_BITCODE = NO; 420 | INFOPLIST_FILE = Runner/Info.plist; 421 | LD_RUNPATH_SEARCH_PATHS = ( 422 | "$(inherited)", 423 | "@executable_path/Frameworks", 424 | ); 425 | PRODUCT_BUNDLE_IDENTIFIER = com.vladih.computervision.flutterVisionExample; 426 | PRODUCT_NAME = "$(TARGET_NAME)"; 427 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 428 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 429 | SWIFT_VERSION = 5.0; 430 | VERSIONING_SYSTEM = "apple-generic"; 431 | }; 432 | name = Debug; 433 | }; 434 | 97C147071CF9000F007C117D /* Release */ = { 435 | isa = XCBuildConfiguration; 436 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | CLANG_ENABLE_MODULES = YES; 440 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 441 | ENABLE_BITCODE = NO; 442 | INFOPLIST_FILE = Runner/Info.plist; 443 | LD_RUNPATH_SEARCH_PATHS = ( 444 | "$(inherited)", 445 | "@executable_path/Frameworks", 446 | ); 447 | PRODUCT_BUNDLE_IDENTIFIER = com.vladih.computervision.flutterVisionExample; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 450 | SWIFT_VERSION = 5.0; 451 | VERSIONING_SYSTEM = "apple-generic"; 452 | }; 453 | name = Release; 454 | }; 455 | /* End XCBuildConfiguration section */ 456 | 457 | /* Begin XCConfigurationList section */ 458 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147031CF9000F007C117D /* Debug */, 462 | 97C147041CF9000F007C117D /* Release */, 463 | 249021D3217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 469 | isa = XCConfigurationList; 470 | buildConfigurations = ( 471 | 97C147061CF9000F007C117D /* Debug */, 472 | 97C147071CF9000F007C117D /* Release */, 473 | 249021D4217E4FDB00AE95B9 /* Profile */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | /* End XCConfigurationList section */ 479 | }; 480 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 481 | } 482 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Flutter Vision 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_vision_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | NSCameraUsageDescription 48 | Access to camera is necesary to run flutter_vision plugin 49 | NSMicrophoneUsageDescription 50 | Access to mycrophone is not necesary but camera plugin require it to run flutter_vision plugin 51 | 52 | 53 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "2.11.0" 12 | boolean_selector: 13 | dependency: transitive 14 | description: 15 | name: boolean_selector 16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.1.1" 20 | camera: 21 | dependency: "direct main" 22 | description: 23 | name: camera 24 | sha256: "3ad71371b8168a4c8012c0b40a53c05afc75d46cc688b0f37b4611a841d47b25" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "0.9.8+1" 28 | camera_android: 29 | dependency: transitive 30 | description: 31 | name: camera_android 32 | sha256: "665d62c1f334722c7519ca5d3b94ad68ecaa801691870602da5638a42c1fff67" 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "0.9.8+3" 36 | camera_avfoundation: 37 | dependency: transitive 38 | description: 39 | name: camera_avfoundation 40 | sha256: "6a68c20593d4cd58974d555f74a48b244f9db28cc9156de57781122d11b8754b" 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "0.9.11" 44 | camera_platform_interface: 45 | dependency: transitive 46 | description: 47 | name: camera_platform_interface 48 | sha256: b632be28e61d00a233f67d98ea90fd7041956f27a1c65500188ee459be60e15f 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "2.4.0" 52 | camera_web: 53 | dependency: transitive 54 | description: 55 | name: camera_web 56 | sha256: "18cdbee5441e9a6fb129fdd9b68a06d1b8c5236932ba97d5faeaefe80db2e5bd" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "0.2.1+6" 60 | characters: 61 | dependency: transitive 62 | description: 63 | name: characters 64 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.3.0" 68 | clock: 69 | dependency: transitive 70 | description: 71 | name: clock 72 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.1.1" 76 | collection: 77 | dependency: transitive 78 | description: 79 | name: collection 80 | sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "1.17.2" 84 | cross_file: 85 | dependency: transitive 86 | description: 87 | name: cross_file 88 | sha256: "0b0036e8cccbfbe0555fd83c1d31a6f30b77a96b598b35a5d36dd41f718695e9" 89 | url: "https://pub.dev" 90 | source: hosted 91 | version: "0.3.3+4" 92 | cupertino_icons: 93 | dependency: "direct main" 94 | description: 95 | name: cupertino_icons 96 | sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be 97 | url: "https://pub.dev" 98 | source: hosted 99 | version: "1.0.5" 100 | fake_async: 101 | dependency: transitive 102 | description: 103 | name: fake_async 104 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" 105 | url: "https://pub.dev" 106 | source: hosted 107 | version: "1.3.1" 108 | ffi: 109 | dependency: transitive 110 | description: 111 | name: ffi 112 | sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978 113 | url: "https://pub.dev" 114 | source: hosted 115 | version: "2.0.1" 116 | file: 117 | dependency: transitive 118 | description: 119 | name: file 120 | sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" 121 | url: "https://pub.dev" 122 | source: hosted 123 | version: "6.1.4" 124 | flutter: 125 | dependency: "direct main" 126 | description: flutter 127 | source: sdk 128 | version: "0.0.0" 129 | flutter_lints: 130 | dependency: "direct dev" 131 | description: 132 | name: flutter_lints 133 | sha256: aeb0b80a8b3709709c9cc496cdc027c5b3216796bc0af0ce1007eaf24464fd4c 134 | url: "https://pub.dev" 135 | source: hosted 136 | version: "2.0.1" 137 | flutter_plugin_android_lifecycle: 138 | dependency: transitive 139 | description: 140 | name: flutter_plugin_android_lifecycle 141 | sha256: "60fc7b78455b94e6de2333d2f95196d32cf5c22f4b0b0520a628804cb463503b" 142 | url: "https://pub.dev" 143 | source: hosted 144 | version: "2.0.7" 145 | flutter_speed_dial: 146 | dependency: "direct main" 147 | description: 148 | name: flutter_speed_dial 149 | sha256: "41d7ad0bc224248637b3a5e0b9083e912a75445bdb450cf82b8ed06d7af7c61d" 150 | url: "https://pub.dev" 151 | source: hosted 152 | version: "6.2.0" 153 | flutter_test: 154 | dependency: "direct dev" 155 | description: flutter 156 | source: sdk 157 | version: "0.0.0" 158 | flutter_vision: 159 | dependency: "direct main" 160 | description: 161 | path: ".." 162 | relative: true 163 | source: path 164 | version: "1.1.2" 165 | flutter_web_plugins: 166 | dependency: transitive 167 | description: flutter 168 | source: sdk 169 | version: "0.0.0" 170 | http: 171 | dependency: transitive 172 | description: 173 | name: http 174 | sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" 175 | url: "https://pub.dev" 176 | source: hosted 177 | version: "0.13.5" 178 | http_parser: 179 | dependency: transitive 180 | description: 181 | name: http_parser 182 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" 183 | url: "https://pub.dev" 184 | source: hosted 185 | version: "4.0.2" 186 | image_picker: 187 | dependency: "direct main" 188 | description: 189 | name: image_picker 190 | sha256: "22207768556b82d55ec70166824350fee32298732d5efa4d6e756f848f51f66a" 191 | url: "https://pub.dev" 192 | source: hosted 193 | version: "0.8.6+3" 194 | image_picker_android: 195 | dependency: transitive 196 | description: 197 | name: image_picker_android 198 | sha256: "68d067baf7f6e401b1124ee83dd6967e67847314250fd68012aab34a69beb344" 199 | url: "https://pub.dev" 200 | source: hosted 201 | version: "0.8.5+7" 202 | image_picker_for_web: 203 | dependency: transitive 204 | description: 205 | name: image_picker_for_web 206 | sha256: "66fc6e3877bbde82c33d122f3588777c3784ac5bd7d1cdd79213ef7aecb85b34" 207 | url: "https://pub.dev" 208 | source: hosted 209 | version: "2.1.11" 210 | image_picker_ios: 211 | dependency: transitive 212 | description: 213 | name: image_picker_ios 214 | sha256: "39aa70b5f1e5e7c94585b9738632d5fdb764a5655e40cd9e7b95fbd2fc50c519" 215 | url: "https://pub.dev" 216 | source: hosted 217 | version: "0.8.6+9" 218 | image_picker_platform_interface: 219 | dependency: transitive 220 | description: 221 | name: image_picker_platform_interface 222 | sha256: "1991219d9dbc42a99aff77e663af8ca51ced592cd6685c9485e3458302d3d4f8" 223 | url: "https://pub.dev" 224 | source: hosted 225 | version: "2.6.3" 226 | js: 227 | dependency: transitive 228 | description: 229 | name: js 230 | sha256: a5e201311cb08bf3912ebbe9a2be096e182d703f881136ec1e81a2338a9e120d 231 | url: "https://pub.dev" 232 | source: hosted 233 | version: "0.6.4" 234 | lints: 235 | dependency: transitive 236 | description: 237 | name: lints 238 | sha256: "5e4a9cd06d447758280a8ac2405101e0e2094d2a1dbdd3756aec3fe7775ba593" 239 | url: "https://pub.dev" 240 | source: hosted 241 | version: "2.0.1" 242 | matcher: 243 | dependency: transitive 244 | description: 245 | name: matcher 246 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" 247 | url: "https://pub.dev" 248 | source: hosted 249 | version: "0.12.16" 250 | material_color_utilities: 251 | dependency: transitive 252 | description: 253 | name: material_color_utilities 254 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" 255 | url: "https://pub.dev" 256 | source: hosted 257 | version: "0.5.0" 258 | meta: 259 | dependency: transitive 260 | description: 261 | name: meta 262 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" 263 | url: "https://pub.dev" 264 | source: hosted 265 | version: "1.9.1" 266 | path: 267 | dependency: transitive 268 | description: 269 | name: path 270 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" 271 | url: "https://pub.dev" 272 | source: hosted 273 | version: "1.8.3" 274 | path_provider: 275 | dependency: transitive 276 | description: 277 | name: path_provider 278 | sha256: dcea5feb97d8abf90cab9e9030b497fb7c3cbf26b7a1fe9e3ef7dcb0a1ddec95 279 | url: "https://pub.dev" 280 | source: hosted 281 | version: "2.0.12" 282 | path_provider_android: 283 | dependency: transitive 284 | description: 285 | name: path_provider_android 286 | sha256: a776c088d671b27f6e3aa8881d64b87b3e80201c64e8869b811325de7a76c15e 287 | url: "https://pub.dev" 288 | source: hosted 289 | version: "2.0.22" 290 | path_provider_foundation: 291 | dependency: transitive 292 | description: 293 | name: path_provider_foundation 294 | sha256: "62a68e7e1c6c459f9289859e2fae58290c981ce21d1697faf54910fe1faa4c74" 295 | url: "https://pub.dev" 296 | source: hosted 297 | version: "2.1.1" 298 | path_provider_linux: 299 | dependency: transitive 300 | description: 301 | name: path_provider_linux 302 | sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9" 303 | url: "https://pub.dev" 304 | source: hosted 305 | version: "2.1.8" 306 | path_provider_platform_interface: 307 | dependency: transitive 308 | description: 309 | name: path_provider_platform_interface 310 | sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76 311 | url: "https://pub.dev" 312 | source: hosted 313 | version: "2.0.5" 314 | path_provider_windows: 315 | dependency: transitive 316 | description: 317 | name: path_provider_windows 318 | sha256: bcabbe399d4042b8ee687e17548d5d3f527255253b4a639f5f8d2094a9c2b45c 319 | url: "https://pub.dev" 320 | source: hosted 321 | version: "2.1.3" 322 | platform: 323 | dependency: transitive 324 | description: 325 | name: platform 326 | sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76" 327 | url: "https://pub.dev" 328 | source: hosted 329 | version: "3.1.0" 330 | plugin_platform_interface: 331 | dependency: transitive 332 | description: 333 | name: plugin_platform_interface 334 | sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a 335 | url: "https://pub.dev" 336 | source: hosted 337 | version: "2.1.3" 338 | process: 339 | dependency: transitive 340 | description: 341 | name: process 342 | sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" 343 | url: "https://pub.dev" 344 | source: hosted 345 | version: "4.2.4" 346 | quiver: 347 | dependency: transitive 348 | description: 349 | name: quiver 350 | sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 351 | url: "https://pub.dev" 352 | source: hosted 353 | version: "3.2.1" 354 | sky_engine: 355 | dependency: transitive 356 | description: flutter 357 | source: sdk 358 | version: "0.0.99" 359 | source_span: 360 | dependency: transitive 361 | description: 362 | name: source_span 363 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" 364 | url: "https://pub.dev" 365 | source: hosted 366 | version: "1.10.0" 367 | stack_trace: 368 | dependency: transitive 369 | description: 370 | name: stack_trace 371 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 372 | url: "https://pub.dev" 373 | source: hosted 374 | version: "1.11.0" 375 | stream_channel: 376 | dependency: transitive 377 | description: 378 | name: stream_channel 379 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" 380 | url: "https://pub.dev" 381 | source: hosted 382 | version: "2.1.1" 383 | stream_transform: 384 | dependency: transitive 385 | description: 386 | name: stream_transform 387 | sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" 388 | url: "https://pub.dev" 389 | source: hosted 390 | version: "2.1.0" 391 | string_scanner: 392 | dependency: transitive 393 | description: 394 | name: string_scanner 395 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" 396 | url: "https://pub.dev" 397 | source: hosted 398 | version: "1.2.0" 399 | term_glyph: 400 | dependency: transitive 401 | description: 402 | name: term_glyph 403 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 404 | url: "https://pub.dev" 405 | source: hosted 406 | version: "1.2.1" 407 | test_api: 408 | dependency: transitive 409 | description: 410 | name: test_api 411 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" 412 | url: "https://pub.dev" 413 | source: hosted 414 | version: "0.6.0" 415 | typed_data: 416 | dependency: transitive 417 | description: 418 | name: typed_data 419 | sha256: "26f87ade979c47a150c9eaab93ccd2bebe70a27dc0b4b29517f2904f04eb11a5" 420 | url: "https://pub.dev" 421 | source: hosted 422 | version: "1.3.1" 423 | vector_math: 424 | dependency: transitive 425 | description: 426 | name: vector_math 427 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 428 | url: "https://pub.dev" 429 | source: hosted 430 | version: "2.1.4" 431 | web: 432 | dependency: transitive 433 | description: 434 | name: web 435 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 436 | url: "https://pub.dev" 437 | source: hosted 438 | version: "0.1.4-beta" 439 | win32: 440 | dependency: transitive 441 | description: 442 | name: win32 443 | sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46 444 | url: "https://pub.dev" 445 | source: hosted 446 | version: "3.1.3" 447 | xdg_directories: 448 | dependency: transitive 449 | description: 450 | name: xdg_directories 451 | sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1 452 | url: "https://pub.dev" 453 | source: hosted 454 | version: "1.0.0" 455 | sdks: 456 | dart: ">=3.1.0-185.0.dev <4.0.0" 457 | flutter: ">=3.0.0" 458 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_vision_example 2 | description: Demonstrates how to use the flutter_vision plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | environment: 9 | sdk: ">=2.17.1 <3.0.0" 10 | 11 | # Dependencies specify other packages that your package needs in order to work. 12 | # To automatically upgrade your package dependencies to the latest versions 13 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 14 | # dependencies can be manually updated by changing the version numbers below to 15 | # the latest version available on pub.dev. To see which dependencies have newer 16 | # versions available, run `flutter pub outdated`. 17 | dependencies: 18 | camera: ^0.9.8+1 19 | cupertino_icons: ^1.0.5 20 | flutter: 21 | sdk: flutter 22 | flutter_vision: 23 | path: ../ 24 | flutter_speed_dial: ^6.2.0 25 | image_picker: ^0.8.6+3 26 | 27 | dev_dependencies: 28 | # The "flutter_lints" package below contains a set of recommended lints to 29 | # encourage good coding practices. The lint set provided by the package is 30 | # activated in the `analysis_options.yaml` file located at the root of your 31 | # package. See that file for information about deactivating specific lint 32 | # rules and activating additional ones. 33 | flutter_lints: ^2.0.1 34 | flutter_test: 35 | sdk: flutter 36 | 37 | # For information on the generic Dart part of this file, see the 38 | # following page: https://dart.dev/tools/pub/pubspec 39 | # The following section is specific to Flutter. 40 | flutter: 41 | # The following line ensures that the Material Icons font is 42 | # included with your application, so that you can use the icons in 43 | # the material Icons class. 44 | uses-material-design: true 45 | 46 | # To add assets to your application, add an assets section, like this: 47 | assets: 48 | - assets/ 49 | - assets/labels.txt 50 | - assets/yolov5n.tflite 51 | - assets/tessdata/ 52 | # An image asset can refer to one or more resolution-specific "variants", see 53 | # https://flutter.dev/assets-and-images/#resolution-aware. 54 | # For details regarding adding assets from package dependencies, see 55 | # https://flutter.dev/assets-and-images/#from-packages 56 | # To add custom fonts to your application, add a fonts section here, 57 | # in this "flutter" section. Each entry in this list should have a 58 | # "family" key with the font family name, and a "fonts" key with a 59 | # list giving the asset and other descriptors for the font. For 60 | # example: 61 | # fonts: 62 | # - family: Schyler 63 | # fonts: 64 | # - asset: fonts/Schyler-Regular.ttf 65 | # - asset: fonts/Schyler-Italic.ttf 66 | # style: italic 67 | # - family: Trajan Pro 68 | # fonts: 69 | # - asset: fonts/TrajanPro.ttf 70 | # - asset: fonts/TrajanPro_Bold.ttf 71 | # weight: 700 72 | # 73 | # For details regarding fonts from package dependencies, 74 | # see https://flutter.dev/custom-fonts/#from-packages 75 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | // import 'package:flutter_vision_example/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Verify Platform version', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | //await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that platform version is retrieved. 19 | expect( 20 | find.byWidgetPredicate( 21 | (Widget widget) => 22 | widget is Text && widget.data!.startsWith('Running on:'), 23 | ), 24 | findsOneWidget, 25 | ); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladiH/flutter_vision/9fb58e64a0771d8a7f532730208ea89bd0ff0780/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/FlutterVisionPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface FlutterVisionPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/FlutterVisionPlugin.m: -------------------------------------------------------------------------------- 1 | #import "FlutterVisionPlugin.h" 2 | #if __has_include() 3 | #import 4 | #else 5 | // Support project import fallback if the generated compatibility header 6 | // is not copied when this plugin is created as a library. 7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 8 | #import "flutter_vision-Swift.h" 9 | #endif 10 | 11 | @implementation FlutterVisionPlugin 12 | + (void)registerWithRegistrar:(NSObject*)registrar { 13 | [SwiftFlutterVisionPlugin registerWithRegistrar:registrar]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /ios/Classes/SwiftFlutterVisionPlugin.swift: -------------------------------------------------------------------------------- 1 | import Flutter 2 | import UIKit 3 | 4 | public class SwiftFlutterVisionPlugin: NSObject, FlutterPlugin { 5 | public static func register(with registrar: FlutterPluginRegistrar) { 6 | let channel = FlutterMethodChannel(name: "flutter_vision", binaryMessenger: registrar.messenger()) 7 | let instance = SwiftFlutterVisionPlugin() 8 | registrar.addMethodCallDelegate(instance, channel: channel) 9 | } 10 | 11 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { 12 | result("iOS " + UIDevice.current.systemVersion) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ios/flutter_vision.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. 3 | # Run `pod lib lint flutter_vision.podspec` to validate before publishing. 4 | # 5 | Pod::Spec.new do |s| 6 | s.name = 'flutter_vision' 7 | s.version = '0.0.1' 8 | s.summary = 'A new flutter plugin project.' 9 | s.description = <<-DESC 10 | A new flutter plugin project. 11 | DESC 12 | s.homepage = 'http://example.com' 13 | s.license = { :file => '../LICENSE' } 14 | s.author = { 'Your Company' => 'email@example.com' } 15 | s.source = { :path => '.' } 16 | s.source_files = 'Classes/**/*' 17 | s.dependency 'Flutter' 18 | s.platform = :ios, '9.0' 19 | 20 | # Flutter.framework does not contain a i386 slice. 21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } 22 | s.swift_version = '5.0' 23 | end 24 | -------------------------------------------------------------------------------- /lib/flutter_vision.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | 5 | import 'package:flutter_vision/src/plugin/android.dart'; 6 | 7 | abstract class FlutterVision { 8 | factory FlutterVision() { 9 | switch (Platform.operatingSystem) { 10 | case 'android': 11 | return AndroidFlutterVision(); 12 | case 'ios': 13 | throw UnimplementedError('iOS is not supported for now'); 14 | //return IosDniScanner(); 15 | default: 16 | throw UnsupportedError('Unsupported platform'); 17 | } 18 | } 19 | 20 | // ///loadOcrModel: loads both YOLOv5 and Tesseract4 model from the assets folder and 21 | // ///return a ResponseHandler object. 22 | // /// 23 | // ///if the load is successful, it returns a ResponseHandler as a success object, 24 | // ///otherwise it returns a ResponseHandler as an error object 25 | // ///```json:{ 26 | // /// "type": "success" or "error", 27 | // /// "message": "ok", 28 | // /// "data": {}``` 29 | // /// 30 | // /// args: [modelPath] - path to the model file 31 | // /// ,[labelsPath] - path to the labels file 32 | // /// ,[numThreads] - number of threads to use for inference 33 | // /// ,[useGPU] - use GPU for inference 34 | // /// ,[language] - language for tesseract4(en,spa,de,fr,it,nl,ru,pt,tr,zh) 35 | // /// ,[tesseract4Config] - tesseract4 config 36 | // Future loadOcrModel( 37 | // {required String modelPath, 38 | // required String labels, 39 | // int? numThreads, 40 | // bool? useGpu, 41 | // String? language, 42 | // Map? args}); 43 | 44 | // ///scanOnFrame accept a byte List as input and 45 | // ///return a ResponseHandler object. 46 | // /// 47 | // ///if scanOnFrame run without error, it returns a ResponseHandler as a success object, 48 | // ///otherwise it returns a ResponseHandler as an error object. 49 | // /// 50 | // ///```json:{ 51 | // /// "type": 'success', 52 | // /// "message": "ok", 53 | // /// "data": List> 54 | // /// }``` 55 | // ///where map is mapped as follows: 56 | // /// 57 | // ///```Map:{ 58 | // /// "confidence": double, 59 | // /// "box": {x1:double, y1:double, x2:double, y2:double}, 60 | // /// "text": String, 61 | // /// "image": Uint8List, 62 | // /// "tag": String 63 | // /// }``` 64 | // /// 65 | // ///args: [bytesList] - image as byte list 66 | // ///, [imageHeight] - image height 67 | // ///, [imageWidth] - image width 68 | // ///, [classIsText] - list of classes to be detected as text 69 | // ///, [iouThreshold] - intersection over union threshold 70 | // ///, [confThreshold] - confidence threshold 71 | // Future>> ocrOnFrame({ 72 | // required List bytesList, 73 | // required int imageHeight, 74 | // required int imageWidth, 75 | // required List classIsText, 76 | // double? iouThreshold, 77 | // double? confThreshold, 78 | // }); 79 | 80 | // /// dispose OCRModel, clean and save resources 81 | // Future closeOcrModel(); 82 | 83 | ///loadYoloModel: load YOLOv5 model from the assets folder 84 | /// 85 | /// args: [modelPath] - path to the model file 86 | /// ,[labelsPath] - path to the labels file 87 | /// ,[modelVersion] - yolov5, yolov8 88 | /// ,[quantization] - When set to true, quantized models are used, which can result in faster execution, reduced memory usage, and slightly lower accuracy. 89 | /// ,[numThreads] - number of threads to use for inference 90 | /// ,[useGPU] - use GPU for inference 91 | Future loadYoloModel( 92 | {required String modelPath, 93 | required String labels, 94 | required String modelVersion, 95 | bool? quantization, 96 | int? numThreads, 97 | bool? useGpu}); 98 | 99 | ///yoloOnFrame accept a byte List as input and 100 | ///return a List>. 101 | /// 102 | ///where map is mapped as follow: 103 | /// 104 | ///```Map:{ 105 | /// "box": [x1:left, y1:top, x2:right, y2:bottom, class_confidence] 106 | /// "tag": String: detected class 107 | /// }``` 108 | /// 109 | ///args: [bytesList] - image as byte list 110 | ///, [imageHeight] - image height 111 | ///, [imageWidth] - image width 112 | ///, [iouThreshold] - intersection over union threshold, default 0.4 113 | ///, [confThreshold] - model confidence threshold, default 0.5, only for [yolov5] 114 | ///, [classThreshold] - class confidence threshold, default 0.5 115 | Future>> yoloOnFrame({ 116 | required List bytesList, 117 | required int imageHeight, 118 | required int imageWidth, 119 | double? iouThreshold, 120 | double? confThreshold, 121 | double? classThreshold, 122 | }); 123 | 124 | ///yoloOnImage accept a Uint8List as input and 125 | ///return a List>. 126 | /// 127 | ///where map is mapped as follows: 128 | /// 129 | ///```Map:{ 130 | /// "box": [x1:left, y1:top, x2:right, y2:bottom, class_confidence] 131 | /// "tag": String: detected class 132 | /// }``` 133 | /// 134 | ///args: [bytesList] - image bytes 135 | ///, [imageHeight] - image height 136 | ///, [imageWidth] - image width 137 | ///, [iouThreshold] - intersection over union threshold, default 0.4 138 | ///, [confThreshold] - model confidence threshold, default 0.5, only for [yolov5] 139 | ///, [classThreshold] - class confidence threshold, default 0.5 140 | Future>> yoloOnImage({ 141 | required Uint8List bytesList, 142 | required int imageHeight, 143 | required int imageWidth, 144 | double? iouThreshold, 145 | double? confThreshold, 146 | double? classThreshold, 147 | }); 148 | 149 | /// dispose OCRModel, clean and save resources 150 | Future closeYoloModel(); 151 | 152 | ///loadTesseractModel: load Tesseract5 model from the assets folder. 153 | /// 154 | /// ,[language] - language for tesseract4(en,spa,de,fr,it,nl,ru,pt,tr,zh) 155 | /// ,[tesseract4Config] - tesseract4 config 156 | Future loadTesseractModel( 157 | {String? language, Map? args}); 158 | 159 | ///tesseractOnImage accept a byte as input and 160 | ///return a List. 161 | /// 162 | ///where map is mapped as follows: 163 | /// 164 | ///```Map:{ 165 | /// "text": String 166 | /// "word_conf": List:int 167 | /// "mean_conf": int 168 | /// }``` 169 | /// 170 | ///args: [bytesList] - image as byte 171 | Future>> tesseractOnImage( 172 | {required Uint8List bytesList}); 173 | 174 | /// dispose Tesseract model, clean and save resources 175 | Future closeTesseractModel(); 176 | } 177 | -------------------------------------------------------------------------------- /lib/src/plugin/android.dart: -------------------------------------------------------------------------------- 1 | import 'dart:typed_data'; 2 | 3 | import '../../flutter_vision.dart'; 4 | import 'base.dart'; 5 | 6 | class AndroidFlutterVision extends BaseFlutterVision implements FlutterVision { 7 | // @override 8 | // Future loadOcrModel( 9 | // {required String modelPath, 10 | // required String labels, 11 | // int? numThreads, 12 | // bool? useGpu, 13 | // String? language, 14 | // Map? args}) async { 15 | // try { 16 | // final String testData = await loadTessData(); 17 | // await channel.invokeMethod('loadOcrModel', { 18 | // 'model_path': modelPath, 19 | // 'is_asset': true, 20 | // 'num_threads': numThreads ?? 1, 21 | // 'use_gpu': useGpu ?? false, 22 | // 'label_path': labels, 23 | // 'image_mean': 0.0, 24 | // 'image_std': 255.0, 25 | // 'rotation': 90, 26 | // 'tess_data': testData, 27 | // 'arg': args, 28 | // 'language': language ?? 'eng' 29 | // }); 30 | // } catch (e) { 31 | // rethrow; 32 | // } 33 | // } 34 | 35 | // @override 36 | // Future>> ocrOnFrame({ 37 | // required List bytesList, 38 | // required int imageHeight, 39 | // required int imageWidth, 40 | // required List classIsText, 41 | // double? iouThreshold, 42 | // double? confThreshold, 43 | // }) async { 44 | // try { 45 | // return await _ocrOnFrame( 46 | // bytesList: bytesList, 47 | // imageHeight: imageHeight, 48 | // imageWidth: imageWidth, 49 | // iouThreshold: iouThreshold ?? 0.4, 50 | // confThreshold: confThreshold ?? 0.5, 51 | // classIsText: classIsText); 52 | // } catch (e) { 53 | // rethrow; 54 | // } 55 | // } 56 | 57 | // Future>> _ocrOnFrame({ 58 | // required List bytesList, 59 | // required int imageHeight, 60 | // required int imageWidth, 61 | // required double iouThreshold, 62 | // required double confThreshold, 63 | // required List classIsText, 64 | // }) async { 65 | // try { 66 | // final x = await channel.invokeMethod>>( 67 | // 'ocrOnFrame', 68 | // { 69 | // "bytesList": bytesList, 70 | // "image_height": imageHeight, 71 | // "image_width": imageWidth, 72 | // "iou_threshold": iouThreshold, 73 | // "conf_threshold": confThreshold, 74 | // "class_is_text": classIsText 75 | // }, 76 | // ); 77 | // return x ?? []; 78 | // } catch (e) { 79 | // rethrow; 80 | // } 81 | // } 82 | 83 | @override 84 | Future loadYoloModel( 85 | {required String modelPath, 86 | required String labels, 87 | required String modelVersion, 88 | bool? quantization, 89 | int? numThreads, 90 | bool? useGpu}) async { 91 | try { 92 | await channel.invokeMethod('loadYoloModel', { 93 | 'model_path': modelPath, 94 | 'is_asset': true, 95 | 'quantization': quantization ?? false, 96 | 'num_threads': numThreads ?? 1, 97 | 'use_gpu': useGpu ?? false, 98 | 'label_path': labels, 99 | 'rotation': 90, 100 | 'model_version': modelVersion 101 | }); 102 | } catch (e) { 103 | rethrow; 104 | } 105 | } 106 | 107 | @override 108 | Future>> yoloOnFrame({ 109 | required List bytesList, 110 | required int imageHeight, 111 | required int imageWidth, 112 | double? iouThreshold, 113 | double? confThreshold, 114 | double? classThreshold, 115 | }) async { 116 | try { 117 | return (await _yoloOnFrame( 118 | bytesList: bytesList, 119 | imageHeight: imageHeight, 120 | imageWidth: imageWidth, 121 | iouThreshold: iouThreshold ?? 0.4, 122 | confThreshold: confThreshold ?? 0.5, 123 | classThreshold: classThreshold ?? 0.5)); 124 | } catch (e) { 125 | rethrow; 126 | } 127 | } 128 | 129 | Future>> _yoloOnFrame({ 130 | required List bytesList, 131 | required int imageHeight, 132 | required int imageWidth, 133 | required double iouThreshold, 134 | required double confThreshold, 135 | required double classThreshold, 136 | }) async { 137 | try { 138 | final x = await channel.invokeMethod>( 139 | 'yoloOnFrame', 140 | { 141 | "bytesList": bytesList, 142 | "image_height": imageHeight, 143 | "image_width": imageWidth, 144 | "iou_threshold": iouThreshold, 145 | "conf_threshold": confThreshold, 146 | "class_threshold": classThreshold 147 | }, 148 | ); 149 | return x?.isNotEmpty ?? false 150 | ? x!.map((e) => Map.from(e)).toList() 151 | : []; 152 | } catch (e) { 153 | // print(e); 154 | rethrow; 155 | } 156 | } 157 | 158 | @override 159 | Future>> yoloOnImage({ 160 | required Uint8List bytesList, 161 | required int imageHeight, 162 | required int imageWidth, 163 | double? iouThreshold, 164 | double? confThreshold, 165 | double? classThreshold, 166 | }) async { 167 | try { 168 | return await _yoloOnImage( 169 | bytesList: bytesList, 170 | imageHeight: imageHeight, 171 | imageWidth: imageWidth, 172 | iouThreshold: iouThreshold ?? 0.4, 173 | confThreshold: confThreshold ?? 0.5, 174 | classThreshold: classThreshold ?? 0.5); 175 | } catch (e) { 176 | rethrow; 177 | } 178 | } 179 | 180 | Future>> _yoloOnImage({ 181 | required Uint8List bytesList, 182 | required int imageHeight, 183 | required int imageWidth, 184 | required double iouThreshold, 185 | required double confThreshold, 186 | required double classThreshold, 187 | }) async { 188 | try { 189 | final x = await channel.invokeMethod>( 190 | 'yoloOnImage', 191 | { 192 | "bytesList": bytesList, 193 | "image_height": imageHeight, 194 | "image_width": imageWidth, 195 | "iou_threshold": iouThreshold, 196 | "conf_threshold": confThreshold, 197 | "class_threshold": classThreshold 198 | }, 199 | ); 200 | return x?.isNotEmpty ?? false 201 | ? x!.map((e) => Map.from(e)).toList() 202 | : []; 203 | } catch (e) { 204 | rethrow; 205 | } 206 | } 207 | 208 | @override 209 | Future loadTesseractModel( 210 | {String? language, Map? args}) async { 211 | try { 212 | final String testData = await loadTessData(); 213 | await channel.invokeMethod('loadTesseractModel', 214 | {'tess_data': testData, 'arg': args, 'language': language ?? 'eng'}); 215 | } catch (e) { 216 | rethrow; 217 | } 218 | } 219 | 220 | @override 221 | Future>> tesseractOnImage({ 222 | required Uint8List bytesList, 223 | }) async { 224 | try { 225 | return await _tesseractOnImage(bytesList: bytesList); 226 | } catch (e) { 227 | rethrow; 228 | } 229 | } 230 | 231 | Future>> _tesseractOnImage( 232 | {required Uint8List bytesList}) async { 233 | try { 234 | final x = await channel.invokeMethod( 235 | 'tesseractOnImage', 236 | { 237 | "bytesList": bytesList, 238 | }, 239 | ); 240 | return x == null ? [] : [Map.from(x)]; 241 | } catch (e) { 242 | rethrow; 243 | } 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /lib/src/plugin/base.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:path/path.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:path_provider/path_provider.dart'; 6 | 7 | abstract class BaseFlutterVision { 8 | // ignore: constant_identifier_names 9 | static const String TESS_DATA_CONFIG = 'assets/tessdata_config.json'; 10 | // ignore: constant_identifier_names 11 | static const String TESS_DATA_PATH = 'assets/tessdata'; 12 | static const MethodChannel _channel = MethodChannel('flutter_vision'); 13 | MethodChannel get channel => _channel; 14 | 15 | Future loadTessData() async { 16 | try { 17 | final Directory appDirectory = await getApplicationDocumentsDirectory(); 18 | final String tessdataDirectory = join(appDirectory.path, 'tessdata'); 19 | 20 | if (!await Directory(tessdataDirectory).exists()) { 21 | await Directory(tessdataDirectory).create(); 22 | } 23 | await copyTessDataToAppDocumentsDirectory(tessdataDirectory); 24 | return appDirectory.path; 25 | } catch (e) { 26 | rethrow; 27 | } 28 | } 29 | 30 | Future copyTessDataToAppDocumentsDirectory(String tessdataDirectory) async { 31 | final String config = await rootBundle.loadString(TESS_DATA_CONFIG); 32 | Map files = jsonDecode(config); 33 | for (var file in files["files"]) { 34 | if (!await File('$tessdataDirectory/$file').exists()) { 35 | final ByteData data = await rootBundle.load('$TESS_DATA_PATH/$file'); 36 | final Uint8List bytes = data.buffer.asUint8List( 37 | data.offsetInBytes, 38 | data.lengthInBytes, 39 | ); 40 | await File('$tessdataDirectory/$file').writeAsBytes(bytes); 41 | } 42 | } 43 | } 44 | 45 | // Future loadOcrModel( 46 | // {required String modelPath, 47 | // required String labels, 48 | // int? numThreads, 49 | // bool? useGpu, 50 | // String? language, 51 | // Map? args}); 52 | 53 | // Future>> ocrOnFrame({ 54 | // required List bytesList, 55 | // required int imageHeight, 56 | // required int imageWidth, 57 | // required List classIsText, 58 | // double? iouThreshold, 59 | // double? confThreshold, 60 | // }); 61 | 62 | // Future closeOcrModel() async { 63 | // await channel.invokeMethod('closeOcrModel'); 64 | // } 65 | 66 | Future loadYoloModel({ 67 | required String modelPath, 68 | required String labels, 69 | required String modelVersion, 70 | bool? quantization, 71 | int? numThreads, 72 | bool? useGpu, 73 | }); 74 | 75 | Future>> yoloOnFrame({ 76 | required List bytesList, 77 | required int imageHeight, 78 | required int imageWidth, 79 | double? iouThreshold, 80 | double? confThreshold, 81 | double? classThreshold, 82 | }); 83 | 84 | Future>> yoloOnImage({ 85 | required Uint8List bytesList, 86 | required int imageHeight, 87 | required int imageWidth, 88 | double? iouThreshold, 89 | double? confThreshold, 90 | double? classThreshold, 91 | }); 92 | 93 | Future closeYoloModel() async { 94 | await channel.invokeMethod('closeYoloModel'); 95 | } 96 | 97 | Future loadTesseractModel( 98 | {String? language, Map? args}); 99 | 100 | Future>> tesseractOnImage( 101 | {required Uint8List bytesList}); 102 | 103 | Future closeTesseractModel() async { 104 | await channel.invokeMethod('closeTesseractModel'); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_vision 2 | description: Plugin for managing Yolov5, Yolov8 and Tesseract v5 accessing with TensorFlow Lite 2.x. Support object detection, segmentation and OCR on Android. iOS, Working in progress. 3 | 4 | version: 1.1.4 5 | 6 | homepage: https://github.com/vladiH/flutter_vision 7 | 8 | environment: 9 | sdk: ">=2.17.1 <4.0.0" 10 | flutter: ">=2.5.0" 11 | 12 | dependencies: 13 | flutter: 14 | sdk: flutter 15 | path: ^1.8.1 16 | path_provider: ^2.0.11 17 | 18 | dev_dependencies: 19 | flutter_lints: ^2.0.1 20 | flutter_test: 21 | sdk: flutter 22 | # For information on the generic Dart part of this file, see the 23 | # following page: https://dart.dev/tools/pub/pubspec 24 | # The following section is specific to Flutter. 25 | flutter: 26 | # This section identifies this Flutter project as a plugin project. 27 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily 28 | # be modified. They are used by the tooling to maintain consistency when 29 | # adding or updating assets for this project. 30 | plugin: 31 | platforms: 32 | android: 33 | package: com.vladih.computer_vision.flutter_vision 34 | pluginClass: FlutterVisionPlugin 35 | ios: 36 | pluginClass: FlutterVisionPlugin 37 | # To add assets to your plugin package, add an assets section, like this: 38 | # assets: 39 | # - images/a_dot_burr.jpeg 40 | # - images/a_dot_ham.jpeg 41 | # 42 | # For details regarding assets in packages, see 43 | # https://flutter.dev/assets-and-images/#from-packages 44 | # 45 | # An image asset can refer to one or more resolution-specific "variants", see 46 | # https://flutter.dev/assets-and-images/#resolution-aware. 47 | # To add custom fonts to your plugin package, add a fonts section here, 48 | # in this "flutter" section. Each entry in this list should have a 49 | # "family" key with the font family name, and a "fonts" key with a 50 | # list giving the asset and other descriptors for the font. For 51 | # example: 52 | # fonts: 53 | # - family: Schyler 54 | # fonts: 55 | # - asset: fonts/Schyler-Regular.ttf 56 | # - asset: fonts/Schyler-Italic.ttf 57 | # style: italic 58 | # - family: Trajan Pro 59 | # fonts: 60 | # - asset: fonts/TrajanPro.ttf 61 | # - asset: fonts/TrajanPro_Bold.ttf 62 | # weight: 700 63 | # 64 | # For details regarding fonts in packages, see 65 | # https://flutter.dev/custom-fonts/#from-packages 66 | -------------------------------------------------------------------------------- /test/flutter_vision_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | // import 'package:flutter_vision/flutter_vision.dart'; 4 | 5 | void main() { 6 | const MethodChannel channel = MethodChannel('flutter_vision'); 7 | 8 | TestWidgetsFlutterBinding.ensureInitialized(); 9 | 10 | handler(MethodCall methodCall) async { 11 | if (methodCall.method == 'getAll') { 12 | return { 13 | 'appName': 'myapp', 14 | 'packageName': 'com.mycompany.myapp', 15 | 'version': '0.0.1', 16 | 'buildNumber': '1' 17 | }; 18 | } 19 | return null; 20 | } 21 | 22 | TestWidgetsFlutterBinding.ensureInitialized(); 23 | 24 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger 25 | .setMockMethodCallHandler(channel, handler); 26 | } 27 | --------------------------------------------------------------------------------