├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── labels.txt │ │ └── mobilenet_quant_v1_224.tflite │ ├── java │ │ └── com │ │ │ └── google │ │ │ └── firebase │ │ │ └── samples │ │ │ └── apps │ │ │ └── mlkit │ │ │ ├── CameraSource.java │ │ │ ├── CameraSourcePreview.java │ │ │ ├── ChooserActivity.java │ │ │ ├── FrameMetadata.java │ │ │ ├── GraphicOverlay.java │ │ │ ├── LivePreviewActivity.java │ │ │ ├── StillImageActivity.java │ │ │ ├── VisionImageProcessor.java │ │ │ ├── VisionProcessorBase.java │ │ │ ├── barcodescanning │ │ │ ├── BarcodeGraphic.java │ │ │ └── BarcodeScanningProcessor.java │ │ │ ├── cloudimagelabeling │ │ │ ├── CloudImageLabelingProcessor.java │ │ │ └── CloudLabelGraphic.java │ │ │ ├── cloudlandmarkrecognition │ │ │ ├── CloudLandmarkGraphic.java │ │ │ └── CloudLandmarkRecognitionProcessor.java │ │ │ ├── cloudtextrecognition │ │ │ ├── CloudDocumentTextRecognitionProcessor.java │ │ │ ├── CloudTextGraphic.java │ │ │ └── CloudTextRecognitionProcessor.java │ │ │ ├── custommodel │ │ │ ├── CustomImageClassifier.java │ │ │ ├── CustomImageClassifierProcessor.java │ │ │ └── LabelGraphic.java │ │ │ ├── facedetection │ │ │ ├── FaceDetectionProcessor.java │ │ │ └── FaceGraphic.java │ │ │ ├── imagelabeling │ │ │ ├── ImageLabelingProcessor.java │ │ │ └── LabelGraphic.java │ │ │ └── textrecognition │ │ │ ├── TextGraphic.java │ │ │ └── TextRecognitionProcessor.java │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_action_info.png │ │ ├── ic_launcher.png │ │ ├── ic_switch_camera_white_48dp.xml │ │ ├── ic_switch_camera_white_48dp_inset.png │ │ └── tile.9.png │ │ ├── drawable-mdpi │ │ ├── ic_action_info.png │ │ ├── ic_launcher.png │ │ ├── ic_switch_camera_white_48dp.xml │ │ └── ic_switch_camera_white_48dp_inset.png │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ ├── ic_action_info.png │ │ ├── ic_launcher.png │ │ ├── ic_switch_camera_white_48dp.xml │ │ └── ic_switch_camera_white_48dp_inset.png │ │ ├── drawable-xxhdpi │ │ ├── ic_action_info.png │ │ ├── ic_launcher.png │ │ ├── ic_switch_camera_white_48dp.xml │ │ └── ic_switch_camera_white_48dp_inset.png │ │ ├── drawable-xxxhdpi │ │ ├── ic_switch_camera_white_48dp.xml │ │ └── ic_switch_camera_white_48dp_inset.png │ │ ├── drawable │ │ ├── flip_cam_512.png │ │ ├── ic_launcher_background.xml │ │ └── mlkit.png │ │ ├── layout-land │ │ └── activity_live_preview.xml │ │ ├── layout │ │ ├── activity_chooser.xml │ │ ├── activity_live_preview.xml │ │ ├── activity_still_image.xml │ │ ├── spinner_style.xml │ │ └── toggle_style.xml │ │ ├── menu │ │ └── camera_button_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── screen.png ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ML Kit for Firebase Quickstart 2 | ============================== 3 | 4 | The ML Kit for Firebase Android Quickstart app demonstrates how to use the 5 | various features of ML Kit to add machine learning to your application. 6 | 7 | Introduction 8 | ------------ 9 | 10 | - [Read more about ML Kit for Firebase](https://firebase.google.com/docs/ml-kit/) 11 | 12 | Getting Started 13 | --------------- 14 | 15 | - [Add Firebase to your Android Project](https://firebase.google.com/docs/android/setup). 16 | - Run the sample on an Android device. 17 | - Choose LivePreviewActivity to see a demo of the following APIs: 18 | - Face detection 19 | - Text recognition (on-device) 20 | - Barcode scanning 21 | - Image labeling (on-device) 22 | - Landmark recognition 23 | - Choose StillImageActivity to see a demo of the following: 24 | - Image labeling (Cloud) 25 | - Landmark recognition (Cloud) 26 | - Text recognition (Cloud) 27 | - Document text recognition (Cloud) 28 | 29 | Result 30 | ----------- 31 | 32 | 33 | Support 34 | ------- 35 | 36 | - [Stack Overflow](https://stackoverflow.com/questions/tagged/mlkit) 37 | - [Firebase Support](https://firebase.google.com/support/). 38 | 39 | License 40 | ------- 41 | 42 | Copyright 2018 Google, Inc. 43 | 44 | Licensed to the Apache Software Foundation (ASF) under one or more contributor 45 | license agreements. See the NOTICE file distributed with this work for 46 | additional information regarding copyright ownership. The ASF licenses this 47 | file to you under the Apache License, Version 2.0 (the "License"); you may not 48 | use this file except in compliance with the License. You may obtain a copy of 49 | the License at 50 | 51 | http://www.apache.org/licenses/LICENSE-2.0 52 | 53 | Unless required by applicable law or agreed to in writing, software 54 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 55 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 56 | License for the specific language governing permissions and limitations under 57 | the License. 58 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.google.firebase.samples.apps.mlkit" 7 | minSdkVersion 21 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:27.1.1' 24 | implementation 'com.android.support:design:27.1.1' 25 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 29 | 30 | // ML Kit dependencies 31 | implementation 'com.google.firebase:firebase-ml-vision:15.0.0' 32 | implementation 'com.google.firebase:firebase-ml-vision-image-label-model:15.0.0' 33 | implementation 'com.google.firebase:firebase-ml-model-interpreter:15.0.0' 34 | } 35 | apply plugin: 'com.google.gms.google-services' 36 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "199636744573", 4 | "firebase_url": "https://mlkit-demo-ab019.firebaseio.com", 5 | "project_id": "mlkit-demo-ab019", 6 | "storage_bucket": "mlkit-demo-ab019.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:199636744573:android:09546560b4505797", 12 | "android_client_info": { 13 | "package_name": "com.google.firebase.samples.apps.mlkit" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "199636744573-5omkbk053cb4o5a02q1n63bdk491rff4.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyABq_7Obj5jFJT6TZv8jN1h74bzyafA4pw" 25 | } 26 | ], 27 | "services": { 28 | "analytics_service": { 29 | "status": 1 30 | }, 31 | "appinvite_service": { 32 | "status": 1, 33 | "other_platform_oauth_client": [] 34 | }, 35 | "ads_service": { 36 | "status": 2 37 | } 38 | } 39 | } 40 | ], 41 | "configuration_version": "1" 42 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/assets/labels.txt: -------------------------------------------------------------------------------- 1 | background 2 | tench 3 | goldfish 4 | great white shark 5 | tiger shark 6 | hammerhead 7 | electric ray 8 | stingray 9 | cock 10 | hen 11 | ostrich 12 | brambling 13 | goldfinch 14 | house finch 15 | junco 16 | indigo bunting 17 | robin 18 | bulbul 19 | jay 20 | magpie 21 | chickadee 22 | water ouzel 23 | kite 24 | bald eagle 25 | vulture 26 | great grey owl 27 | European fire salamander 28 | common newt 29 | eft 30 | spotted salamander 31 | axolotl 32 | bullfrog 33 | tree frog 34 | tailed frog 35 | loggerhead 36 | leatherback turtle 37 | mud turtle 38 | terrapin 39 | box turtle 40 | banded gecko 41 | common iguana 42 | American chameleon 43 | whiptail 44 | agama 45 | frilled lizard 46 | alligator lizard 47 | Gila monster 48 | green lizard 49 | African chameleon 50 | Komodo dragon 51 | African crocodile 52 | American alligator 53 | triceratops 54 | thunder snake 55 | ringneck snake 56 | hognose snake 57 | green snake 58 | king snake 59 | garter snake 60 | water snake 61 | vine snake 62 | night snake 63 | boa constrictor 64 | rock python 65 | Indian cobra 66 | green mamba 67 | sea snake 68 | horned viper 69 | diamondback 70 | sidewinder 71 | trilobite 72 | harvestman 73 | scorpion 74 | black and gold garden spider 75 | barn spider 76 | garden spider 77 | black widow 78 | tarantula 79 | wolf spider 80 | tick 81 | centipede 82 | black grouse 83 | ptarmigan 84 | ruffed grouse 85 | prairie chicken 86 | peacock 87 | quail 88 | partridge 89 | African grey 90 | macaw 91 | sulphur-crested cockatoo 92 | lorikeet 93 | coucal 94 | bee eater 95 | hornbill 96 | hummingbird 97 | jacamar 98 | toucan 99 | drake 100 | red-breasted merganser 101 | goose 102 | black swan 103 | tusker 104 | echidna 105 | platypus 106 | wallaby 107 | koala 108 | wombat 109 | jellyfish 110 | sea anemone 111 | brain coral 112 | flatworm 113 | nematode 114 | conch 115 | snail 116 | slug 117 | sea slug 118 | chiton 119 | chambered nautilus 120 | Dungeness crab 121 | rock crab 122 | fiddler crab 123 | king crab 124 | American lobster 125 | spiny lobster 126 | crayfish 127 | hermit crab 128 | isopod 129 | white stork 130 | black stork 131 | spoonbill 132 | flamingo 133 | little blue heron 134 | American egret 135 | bittern 136 | crane 137 | limpkin 138 | European gallinule 139 | American coot 140 | bustard 141 | ruddy turnstone 142 | red-backed sandpiper 143 | redshank 144 | dowitcher 145 | oystercatcher 146 | pelican 147 | king penguin 148 | albatross 149 | grey whale 150 | killer whale 151 | dugong 152 | sea lion 153 | Chihuahua 154 | Japanese spaniel 155 | Maltese dog 156 | Pekinese 157 | Shih-Tzu 158 | Blenheim spaniel 159 | papillon 160 | toy terrier 161 | Rhodesian ridgeback 162 | Afghan hound 163 | basset 164 | beagle 165 | bloodhound 166 | bluetick 167 | black-and-tan coonhound 168 | Walker hound 169 | English foxhound 170 | redbone 171 | borzoi 172 | Irish wolfhound 173 | Italian greyhound 174 | whippet 175 | Ibizan hound 176 | Norwegian elkhound 177 | otterhound 178 | Saluki 179 | Scottish deerhound 180 | Weimaraner 181 | Staffordshire bullterrier 182 | American Staffordshire terrier 183 | Bedlington terrier 184 | Border terrier 185 | Kerry blue terrier 186 | Irish terrier 187 | Norfolk terrier 188 | Norwich terrier 189 | Yorkshire terrier 190 | wire-haired fox terrier 191 | Lakeland terrier 192 | Sealyham terrier 193 | Airedale 194 | cairn 195 | Australian terrier 196 | Dandie Dinmont 197 | Boston bull 198 | miniature schnauzer 199 | giant schnauzer 200 | standard schnauzer 201 | Scotch terrier 202 | Tibetan terrier 203 | silky terrier 204 | soft-coated wheaten terrier 205 | West Highland white terrier 206 | Lhasa 207 | flat-coated retriever 208 | curly-coated retriever 209 | golden retriever 210 | Labrador retriever 211 | Chesapeake Bay retriever 212 | German short-haired pointer 213 | vizsla 214 | English setter 215 | Irish setter 216 | Gordon setter 217 | Brittany spaniel 218 | clumber 219 | English springer 220 | Welsh springer spaniel 221 | cocker spaniel 222 | Sussex spaniel 223 | Irish water spaniel 224 | kuvasz 225 | schipperke 226 | groenendael 227 | malinois 228 | briard 229 | kelpie 230 | komondor 231 | Old English sheepdog 232 | Shetland sheepdog 233 | collie 234 | Border collie 235 | Bouvier des Flandres 236 | Rottweiler 237 | German shepherd 238 | Doberman 239 | miniature pinscher 240 | Greater Swiss Mountain dog 241 | Bernese mountain dog 242 | Appenzeller 243 | EntleBucher 244 | boxer 245 | bull mastiff 246 | Tibetan mastiff 247 | French bulldog 248 | Great Dane 249 | Saint Bernard 250 | Eskimo dog 251 | malamute 252 | Siberian husky 253 | dalmatian 254 | affenpinscher 255 | basenji 256 | pug 257 | Leonberg 258 | Newfoundland 259 | Great Pyrenees 260 | Samoyed 261 | Pomeranian 262 | chow 263 | keeshond 264 | Brabancon griffon 265 | Pembroke 266 | Cardigan 267 | toy poodle 268 | miniature poodle 269 | standard poodle 270 | Mexican hairless 271 | timber wolf 272 | white wolf 273 | red wolf 274 | coyote 275 | dingo 276 | dhole 277 | African hunting dog 278 | hyena 279 | red fox 280 | kit fox 281 | Arctic fox 282 | grey fox 283 | tabby 284 | tiger cat 285 | Persian cat 286 | Siamese cat 287 | Egyptian cat 288 | cougar 289 | lynx 290 | leopard 291 | snow leopard 292 | jaguar 293 | lion 294 | tiger 295 | cheetah 296 | brown bear 297 | American black bear 298 | ice bear 299 | sloth bear 300 | mongoose 301 | meerkat 302 | tiger beetle 303 | ladybug 304 | ground beetle 305 | long-horned beetle 306 | leaf beetle 307 | dung beetle 308 | rhinoceros beetle 309 | weevil 310 | fly 311 | bee 312 | ant 313 | grasshopper 314 | cricket 315 | walking stick 316 | cockroach 317 | mantis 318 | cicada 319 | leafhopper 320 | lacewing 321 | dragonfly 322 | damselfly 323 | admiral 324 | ringlet 325 | monarch 326 | cabbage butterfly 327 | sulphur butterfly 328 | lycaenid 329 | starfish 330 | sea urchin 331 | sea cucumber 332 | wood rabbit 333 | hare 334 | Angora 335 | hamster 336 | porcupine 337 | fox squirrel 338 | marmot 339 | beaver 340 | guinea pig 341 | sorrel 342 | zebra 343 | hog 344 | wild boar 345 | warthog 346 | hippopotamus 347 | ox 348 | water buffalo 349 | bison 350 | ram 351 | bighorn 352 | ibex 353 | hartebeest 354 | impala 355 | gazelle 356 | Arabian camel 357 | llama 358 | weasel 359 | mink 360 | polecat 361 | black-footed ferret 362 | otter 363 | skunk 364 | badger 365 | armadillo 366 | three-toed sloth 367 | orangutan 368 | gorilla 369 | chimpanzee 370 | gibbon 371 | siamang 372 | guenon 373 | patas 374 | baboon 375 | macaque 376 | langur 377 | colobus 378 | proboscis monkey 379 | marmoset 380 | capuchin 381 | howler monkey 382 | titi 383 | spider monkey 384 | squirrel monkey 385 | Madagascar cat 386 | indri 387 | Indian elephant 388 | African elephant 389 | lesser panda 390 | giant panda 391 | barracouta 392 | eel 393 | coho 394 | rock beauty 395 | anemone fish 396 | sturgeon 397 | gar 398 | lionfish 399 | puffer 400 | abacus 401 | abaya 402 | academic gown 403 | accordion 404 | acoustic guitar 405 | aircraft carrier 406 | airliner 407 | airship 408 | altar 409 | ambulance 410 | amphibian 411 | analog clock 412 | apiary 413 | apron 414 | ashcan 415 | assault rifle 416 | backpack 417 | bakery 418 | balance beam 419 | balloon 420 | ballpoint 421 | Band Aid 422 | banjo 423 | bannister 424 | barbell 425 | barber chair 426 | barbershop 427 | barn 428 | barometer 429 | barrel 430 | barrow 431 | baseball 432 | basketball 433 | bassinet 434 | bassoon 435 | bathing cap 436 | bath towel 437 | bathtub 438 | beach wagon 439 | beacon 440 | beaker 441 | bearskin 442 | beer bottle 443 | beer glass 444 | bell cote 445 | bib 446 | bicycle-built-for-two 447 | bikini 448 | binder 449 | binoculars 450 | birdhouse 451 | boathouse 452 | bobsled 453 | bolo tie 454 | bonnet 455 | bookcase 456 | bookshop 457 | bottlecap 458 | bow 459 | bow tie 460 | brass 461 | brassiere 462 | breakwater 463 | breastplate 464 | broom 465 | bucket 466 | buckle 467 | bulletproof vest 468 | bullet train 469 | butcher shop 470 | cab 471 | caldron 472 | candle 473 | cannon 474 | canoe 475 | can opener 476 | cardigan 477 | car mirror 478 | carousel 479 | carpenter's kit 480 | carton 481 | car wheel 482 | cash machine 483 | cassette 484 | cassette player 485 | castle 486 | catamaran 487 | CD player 488 | cello 489 | cellular telephone 490 | chain 491 | chainlink fence 492 | chain mail 493 | chain saw 494 | chest 495 | chiffonier 496 | chime 497 | china cabinet 498 | Christmas stocking 499 | church 500 | cinema 501 | cleaver 502 | cliff dwelling 503 | cloak 504 | clog 505 | cocktail shaker 506 | coffee mug 507 | coffeepot 508 | coil 509 | combination lock 510 | computer keyboard 511 | confectionery 512 | container ship 513 | convertible 514 | corkscrew 515 | cornet 516 | cowboy boot 517 | cowboy hat 518 | cradle 519 | crane 520 | crash helmet 521 | crate 522 | crib 523 | Crock Pot 524 | croquet ball 525 | crutch 526 | cuirass 527 | dam 528 | desk 529 | desktop computer 530 | dial telephone 531 | diaper 532 | digital clock 533 | digital watch 534 | dining table 535 | dishrag 536 | dishwasher 537 | disk brake 538 | dock 539 | dogsled 540 | dome 541 | doormat 542 | drilling platform 543 | drum 544 | drumstick 545 | dumbbell 546 | Dutch oven 547 | electric fan 548 | electric guitar 549 | electric locomotive 550 | entertainment center 551 | envelope 552 | espresso maker 553 | face powder 554 | feather boa 555 | file 556 | fireboat 557 | fire engine 558 | fire screen 559 | flagpole 560 | flute 561 | folding chair 562 | football helmet 563 | forklift 564 | fountain 565 | fountain pen 566 | four-poster 567 | freight car 568 | French horn 569 | frying pan 570 | fur coat 571 | garbage truck 572 | gasmask 573 | gas pump 574 | goblet 575 | go-kart 576 | golf ball 577 | golfcart 578 | gondola 579 | gong 580 | gown 581 | grand piano 582 | greenhouse 583 | grille 584 | grocery store 585 | guillotine 586 | hair slide 587 | hair spray 588 | half track 589 | hammer 590 | hamper 591 | hand blower 592 | hand-held computer 593 | handkerchief 594 | hard disc 595 | harmonica 596 | harp 597 | harvester 598 | hatchet 599 | holster 600 | home theater 601 | honeycomb 602 | hook 603 | hoopskirt 604 | horizontal bar 605 | horse cart 606 | hourglass 607 | iPod 608 | iron 609 | jack-o'-lantern 610 | jean 611 | jeep 612 | jersey 613 | jigsaw puzzle 614 | jinrikisha 615 | joystick 616 | kimono 617 | knee pad 618 | knot 619 | lab coat 620 | ladle 621 | lampshade 622 | laptop 623 | lawn mower 624 | lens cap 625 | letter opener 626 | library 627 | lifeboat 628 | lighter 629 | limousine 630 | liner 631 | lipstick 632 | Loafer 633 | lotion 634 | loudspeaker 635 | loupe 636 | lumbermill 637 | magnetic compass 638 | mailbag 639 | mailbox 640 | maillot 641 | maillot 642 | manhole cover 643 | maraca 644 | marimba 645 | mask 646 | matchstick 647 | maypole 648 | maze 649 | measuring cup 650 | medicine chest 651 | megalith 652 | microphone 653 | microwave 654 | military uniform 655 | milk can 656 | minibus 657 | miniskirt 658 | minivan 659 | missile 660 | mitten 661 | mixing bowl 662 | mobile home 663 | Model T 664 | modem 665 | monastery 666 | monitor 667 | moped 668 | mortar 669 | mortarboard 670 | mosque 671 | mosquito net 672 | motor scooter 673 | mountain bike 674 | mountain tent 675 | mouse 676 | mousetrap 677 | moving van 678 | muzzle 679 | nail 680 | neck brace 681 | necklace 682 | nipple 683 | notebook 684 | obelisk 685 | oboe 686 | ocarina 687 | odometer 688 | oil filter 689 | organ 690 | oscilloscope 691 | overskirt 692 | oxcart 693 | oxygen mask 694 | packet 695 | paddle 696 | paddlewheel 697 | padlock 698 | paintbrush 699 | pajama 700 | palace 701 | panpipe 702 | paper towel 703 | parachute 704 | parallel bars 705 | park bench 706 | parking meter 707 | passenger car 708 | patio 709 | pay-phone 710 | pedestal 711 | pencil box 712 | pencil sharpener 713 | perfume 714 | Petri dish 715 | photocopier 716 | pick 717 | pickelhaube 718 | picket fence 719 | pickup 720 | pier 721 | piggy bank 722 | pill bottle 723 | pillow 724 | ping-pong ball 725 | pinwheel 726 | pirate 727 | pitcher 728 | plane 729 | planetarium 730 | plastic bag 731 | plate rack 732 | plow 733 | plunger 734 | Polaroid camera 735 | pole 736 | police van 737 | poncho 738 | pool table 739 | pop bottle 740 | pot 741 | potter's wheel 742 | power drill 743 | prayer rug 744 | printer 745 | prison 746 | projectile 747 | projector 748 | puck 749 | punching bag 750 | purse 751 | quill 752 | quilt 753 | racer 754 | racket 755 | radiator 756 | radio 757 | radio telescope 758 | rain barrel 759 | recreational vehicle 760 | reel 761 | reflex camera 762 | refrigerator 763 | remote control 764 | restaurant 765 | revolver 766 | rifle 767 | rocking chair 768 | rotisserie 769 | rubber eraser 770 | rugby ball 771 | rule 772 | running shoe 773 | safe 774 | safety pin 775 | saltshaker 776 | sandal 777 | sarong 778 | sax 779 | scabbard 780 | scale 781 | school bus 782 | schooner 783 | scoreboard 784 | screen 785 | screw 786 | screwdriver 787 | seat belt 788 | sewing machine 789 | shield 790 | shoe shop 791 | shoji 792 | shopping basket 793 | shopping cart 794 | shovel 795 | shower cap 796 | shower curtain 797 | ski 798 | ski mask 799 | sleeping bag 800 | slide rule 801 | sliding door 802 | slot 803 | snorkel 804 | snowmobile 805 | snowplow 806 | soap dispenser 807 | soccer ball 808 | sock 809 | solar dish 810 | sombrero 811 | soup bowl 812 | space bar 813 | space heater 814 | space shuttle 815 | spatula 816 | speedboat 817 | spider web 818 | spindle 819 | sports car 820 | spotlight 821 | stage 822 | steam locomotive 823 | steel arch bridge 824 | steel drum 825 | stethoscope 826 | stole 827 | stone wall 828 | stopwatch 829 | stove 830 | strainer 831 | streetcar 832 | stretcher 833 | studio couch 834 | stupa 835 | submarine 836 | suit 837 | sundial 838 | sunglass 839 | sunglasses 840 | sunscreen 841 | suspension bridge 842 | swab 843 | sweatshirt 844 | swimming trunks 845 | swing 846 | switch 847 | syringe 848 | table lamp 849 | tank 850 | tape player 851 | teapot 852 | teddy 853 | television 854 | tennis ball 855 | thatch 856 | theater curtain 857 | thimble 858 | thresher 859 | throne 860 | tile roof 861 | toaster 862 | tobacco shop 863 | toilet seat 864 | torch 865 | totem pole 866 | tow truck 867 | toyshop 868 | tractor 869 | trailer truck 870 | tray 871 | trench coat 872 | tricycle 873 | trimaran 874 | tripod 875 | triumphal arch 876 | trolleybus 877 | trombone 878 | tub 879 | turnstile 880 | typewriter keyboard 881 | umbrella 882 | unicycle 883 | upright 884 | vacuum 885 | vase 886 | vault 887 | velvet 888 | vending machine 889 | vestment 890 | viaduct 891 | violin 892 | volleyball 893 | waffle iron 894 | wall clock 895 | wallet 896 | wardrobe 897 | warplane 898 | washbasin 899 | washer 900 | water bottle 901 | water jug 902 | water tower 903 | whiskey jug 904 | whistle 905 | wig 906 | window screen 907 | window shade 908 | Windsor tie 909 | wine bottle 910 | wing 911 | wok 912 | wooden spoon 913 | wool 914 | worm fence 915 | wreck 916 | yawl 917 | yurt 918 | web site 919 | comic book 920 | crossword puzzle 921 | street sign 922 | traffic light 923 | book jacket 924 | menu 925 | plate 926 | guacamole 927 | consomme 928 | hot pot 929 | trifle 930 | ice cream 931 | ice lolly 932 | French loaf 933 | bagel 934 | pretzel 935 | cheeseburger 936 | hotdog 937 | mashed potato 938 | head cabbage 939 | broccoli 940 | cauliflower 941 | zucchini 942 | spaghetti squash 943 | acorn squash 944 | butternut squash 945 | cucumber 946 | artichoke 947 | bell pepper 948 | cardoon 949 | mushroom 950 | Granny Smith 951 | strawberry 952 | orange 953 | lemon 954 | fig 955 | pineapple 956 | banana 957 | jackfruit 958 | custard apple 959 | pomegranate 960 | hay 961 | carbonara 962 | chocolate sauce 963 | dough 964 | meat loaf 965 | pizza 966 | potpie 967 | burrito 968 | red wine 969 | espresso 970 | cup 971 | eggnog 972 | alp 973 | bubble 974 | cliff 975 | coral reef 976 | geyser 977 | lakeside 978 | promontory 979 | sandbar 980 | seashore 981 | valley 982 | volcano 983 | ballplayer 984 | groom 985 | scuba diver 986 | rapeseed 987 | daisy 988 | yellow lady's slipper 989 | corn 990 | acorn 991 | hip 992 | buckeye 993 | coral fungus 994 | agaric 995 | gyromitra 996 | stinkhorn 997 | earthstar 998 | hen-of-the-woods 999 | bolete 1000 | ear 1001 | toilet tissue 1002 | -------------------------------------------------------------------------------- /app/src/main/assets/mobilenet_quant_v1_224.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/assets/mobilenet_quant_v1_224.tflite -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/CameraSource.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.firebase.samples.apps.mlkit; 16 | 17 | import android.Manifest; 18 | import android.annotation.SuppressLint; 19 | import android.app.Activity; 20 | import android.content.Context; 21 | import android.graphics.ImageFormat; 22 | import android.graphics.SurfaceTexture; 23 | import android.hardware.Camera; 24 | import android.hardware.Camera.CameraInfo; 25 | import android.support.annotation.Nullable; 26 | import android.support.annotation.RequiresPermission; 27 | import android.util.Log; 28 | import android.view.Surface; 29 | import android.view.SurfaceHolder; 30 | import android.view.WindowManager; 31 | 32 | import com.google.android.gms.common.images.Size; 33 | 34 | import java.io.IOException; 35 | import java.lang.Thread.State; 36 | import java.nio.ByteBuffer; 37 | import java.util.ArrayList; 38 | import java.util.IdentityHashMap; 39 | import java.util.List; 40 | import java.util.Map; 41 | 42 | /** 43 | * Manages the camera and allows UI updates on top of it (e.g. overlaying extra Graphics or 44 | * displaying extra information). This receives preview frames from the camera at a specified rate, 45 | * sending those frames to child classes' detectors / classifiers as fast as it is able to process. 46 | */ 47 | public class CameraSource { 48 | @SuppressLint("InlinedApi") 49 | public static final int CAMERA_FACING_BACK = CameraInfo.CAMERA_FACING_BACK; 50 | 51 | @SuppressLint("InlinedApi") 52 | public static final int CAMERA_FACING_FRONT = CameraInfo.CAMERA_FACING_FRONT; 53 | 54 | private static final String TAG = "MIDemoApp:CameraSource"; 55 | 56 | /** 57 | * The dummy surface texture must be assigned a chosen name. Since we never use an OpenGL context, 58 | * we can choose any ID we want here. The dummy surface texture is not a crazy hack - it is 59 | * actually how the camera team recommends using the camera without a preview. 60 | */ 61 | private static final int DUMMY_TEXTURE_NAME = 100; 62 | 63 | /** 64 | * If the absolute difference between a preview size aspect ratio and a picture size aspect ratio 65 | * is less than this tolerance, they are considered to be the same aspect ratio. 66 | */ 67 | private static final float ASPECT_RATIO_TOLERANCE = 0.01f; 68 | 69 | protected Activity activity; 70 | 71 | private Camera camera; 72 | 73 | protected int facing = CAMERA_FACING_BACK; 74 | 75 | /** 76 | * Rotation of the device, and thus the associated preview images captured from the device. See 77 | * Frame.Metadata#getRotation(). 78 | */ 79 | private int rotation; 80 | 81 | private Size previewSize; 82 | 83 | // These values may be requested by the caller. Due to hardware limitations, we may need to 84 | // select close, but not exactly the same values for these. 85 | private final float requestedFps = 20.0f; 86 | private final int requestedPreviewWidth = 1280; 87 | private final int requestedPreviewHeight = 960; 88 | private final boolean requestedAutoFocus = true; 89 | 90 | // These instances need to be held onto to avoid GC of their underlying resources. Even though 91 | // these aren't used outside of the method that creates them, they still must have hard 92 | // references maintained to them. 93 | private SurfaceTexture dummySurfaceTexture; 94 | private final GraphicOverlay graphicOverlay; 95 | 96 | // True if a SurfaceTexture is being used for the preview, false if a SurfaceHolder is being 97 | // used for the preview. We want to be compatible back to Gingerbread, but SurfaceTexture 98 | // wasn't introduced until Honeycomb. Since the interface cannot use a SurfaceTexture, if the 99 | // developer wants to display a preview we must use a SurfaceHolder. If the developer doesn't 100 | // want to display a preview we use a SurfaceTexture if we are running at least Honeycomb. 101 | private boolean usingSurfaceTexture; 102 | 103 | /** 104 | * Dedicated thread and associated runnable for calling into the detector with frames, as the 105 | * frames become available from the camera. 106 | */ 107 | private Thread processingThread; 108 | 109 | private final FrameProcessingRunnable processingRunnable; 110 | 111 | private final Object processorLock = new Object(); 112 | // TODO(b/74400062) Re-enable the annotaion 113 | // @GuardedBy("processorLock") 114 | private VisionImageProcessor frameProcessor; 115 | 116 | /** 117 | * Map to convert between a byte array, received from the camera, and its associated byte buffer. 118 | * We use byte buffers internally because this is a more efficient way to call into native code 119 | * later (avoids a potential copy). 120 | * 121 | *

Note: uses IdentityHashMap here instead of HashMap because the behavior of an array's 122 | * equals, hashCode and toString methods is both useless and unexpected. IdentityHashMap enforces 123 | * identity ('==') check on the keys. 124 | */ 125 | private final Map bytesToByteBuffer = new IdentityHashMap<>(); 126 | 127 | public CameraSource(Activity activity, GraphicOverlay overlay) { 128 | this.activity = activity; 129 | graphicOverlay = overlay; 130 | graphicOverlay.clear(); 131 | processingRunnable = new FrameProcessingRunnable(); 132 | } 133 | 134 | // ============================================================================================== 135 | // Public 136 | // ============================================================================================== 137 | 138 | /** Stops the camera and releases the resources of the camera and underlying detector. */ 139 | public void release() { 140 | synchronized (processorLock) { 141 | stop(); 142 | processingRunnable.release(); 143 | cleanScreen(); 144 | 145 | if (frameProcessor != null) { 146 | frameProcessor.stop(); 147 | } 148 | } 149 | } 150 | 151 | /** 152 | * Opens the camera and starts sending preview frames to the underlying detector. The preview 153 | * frames are not displayed. 154 | * 155 | * @throws IOException if the camera's preview texture or display could not be initialized 156 | */ 157 | @RequiresPermission(Manifest.permission.CAMERA) 158 | public synchronized CameraSource start() throws IOException { 159 | if (camera != null) { 160 | return this; 161 | } 162 | 163 | camera = createCamera(); 164 | dummySurfaceTexture = new SurfaceTexture(DUMMY_TEXTURE_NAME); 165 | camera.setPreviewTexture(dummySurfaceTexture); 166 | usingSurfaceTexture = true; 167 | camera.startPreview(); 168 | 169 | processingThread = new Thread(processingRunnable); 170 | processingRunnable.setActive(true); 171 | processingThread.start(); 172 | return this; 173 | } 174 | 175 | /** 176 | * Opens the camera and starts sending preview frames to the underlying detector. The supplied 177 | * surface holder is used for the preview so frames can be displayed to the user. 178 | * 179 | * @param surfaceHolder the surface holder to use for the preview frames 180 | * @throws IOException if the supplied surface holder could not be used as the preview display 181 | */ 182 | @RequiresPermission(Manifest.permission.CAMERA) 183 | public synchronized CameraSource start(SurfaceHolder surfaceHolder) throws IOException { 184 | if (camera != null) { 185 | return this; 186 | } 187 | 188 | camera = createCamera(); 189 | camera.setPreviewDisplay(surfaceHolder); 190 | camera.startPreview(); 191 | 192 | processingThread = new Thread(processingRunnable); 193 | processingRunnable.setActive(true); 194 | processingThread.start(); 195 | 196 | usingSurfaceTexture = false; 197 | return this; 198 | } 199 | 200 | /** 201 | * Closes the camera and stops sending frames to the underlying frame detector. 202 | * 203 | *

This camera source may be restarted again by calling {@link #start()} or {@link 204 | * #start(SurfaceHolder)}. 205 | * 206 | *

Call {@link #release()} instead to completely shut down this camera source and release the 207 | * resources of the underlying detector. 208 | */ 209 | public synchronized void stop() { 210 | processingRunnable.setActive(false); 211 | if (processingThread != null) { 212 | try { 213 | // Wait for the thread to complete to ensure that we can't have multiple threads 214 | // executing at the same time (i.e., which would happen if we called start too 215 | // quickly after stop). 216 | processingThread.join(); 217 | } catch (InterruptedException e) { 218 | Log.d(TAG, "Frame processing thread interrupted on release."); 219 | } 220 | processingThread = null; 221 | } 222 | 223 | if (camera != null) { 224 | camera.stopPreview(); 225 | camera.setPreviewCallbackWithBuffer(null); 226 | try { 227 | if (usingSurfaceTexture) { 228 | camera.setPreviewTexture(null); 229 | } else { 230 | camera.setPreviewDisplay(null); 231 | } 232 | } catch (Exception e) { 233 | Log.e(TAG, "Failed to clear camera preview: " + e); 234 | } 235 | camera.release(); 236 | camera = null; 237 | } 238 | 239 | // Release the reference to any image buffers, since these will no longer be in use. 240 | bytesToByteBuffer.clear(); 241 | } 242 | 243 | /** Changes the facing of the camera. */ 244 | public synchronized void setFacing(int facing) { 245 | if ((facing != CAMERA_FACING_BACK) && (facing != CAMERA_FACING_FRONT)) { 246 | throw new IllegalArgumentException("Invalid camera: " + facing); 247 | } 248 | this.facing = facing; 249 | } 250 | 251 | /** Returns the preview size that is currently in use by the underlying camera. */ 252 | public Size getPreviewSize() { 253 | return previewSize; 254 | } 255 | 256 | /** 257 | * Returns the selected camera; one of {@link #CAMERA_FACING_BACK} or {@link 258 | * #CAMERA_FACING_FRONT}. 259 | */ 260 | public int getCameraFacing() { 261 | return facing; 262 | } 263 | 264 | /** 265 | * Opens the camera and applies the user settings. 266 | * 267 | * @throws IOException if camera cannot be found or preview cannot be processed 268 | */ 269 | @SuppressLint("InlinedApi") 270 | private Camera createCamera() throws IOException { 271 | int requestedCameraId = getIdForRequestedCamera(facing); 272 | if (requestedCameraId == -1) { 273 | throw new IOException("Could not find requested camera."); 274 | } 275 | Camera camera = Camera.open(requestedCameraId); 276 | 277 | SizePair sizePair = selectSizePair(camera, requestedPreviewWidth, requestedPreviewHeight); 278 | if (sizePair == null) { 279 | throw new IOException("Could not find suitable preview size."); 280 | } 281 | Size pictureSize = sizePair.pictureSize(); 282 | previewSize = sizePair.previewSize(); 283 | 284 | int[] previewFpsRange = selectPreviewFpsRange(camera, requestedFps); 285 | if (previewFpsRange == null) { 286 | throw new IOException("Could not find suitable preview frames per second range."); 287 | } 288 | 289 | Camera.Parameters parameters = camera.getParameters(); 290 | 291 | if (pictureSize != null) { 292 | parameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight()); 293 | } 294 | parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight()); 295 | parameters.setPreviewFpsRange( 296 | previewFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], 297 | previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); 298 | parameters.setPreviewFormat(ImageFormat.NV21); 299 | 300 | setRotation(camera, parameters, requestedCameraId); 301 | 302 | if (requestedAutoFocus) { 303 | if (parameters 304 | .getSupportedFocusModes() 305 | .contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) { 306 | parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); 307 | } else { 308 | Log.i(TAG, "Camera auto focus is not supported on this device."); 309 | } 310 | } 311 | 312 | camera.setParameters(parameters); 313 | 314 | // Four frame buffers are needed for working with the camera: 315 | // 316 | // one for the frame that is currently being executed upon in doing detection 317 | // one for the next pending frame to process immediately upon completing detection 318 | // two for the frames that the camera uses to populate future preview images 319 | // 320 | // Through trial and error it appears that two free buffers, in addition to the two buffers 321 | // used in this code, are needed for the camera to work properly. Perhaps the camera has 322 | // one thread for acquiring images, and another thread for calling into user code. If only 323 | // three buffers are used, then the camera will spew thousands of warning messages when 324 | // detection takes a non-trivial amount of time. 325 | camera.setPreviewCallbackWithBuffer(new CameraPreviewCallback()); 326 | camera.addCallbackBuffer(createPreviewBuffer(previewSize)); 327 | camera.addCallbackBuffer(createPreviewBuffer(previewSize)); 328 | camera.addCallbackBuffer(createPreviewBuffer(previewSize)); 329 | camera.addCallbackBuffer(createPreviewBuffer(previewSize)); 330 | 331 | return camera; 332 | } 333 | 334 | /** 335 | * Gets the id for the camera specified by the direction it is facing. Returns -1 if no such 336 | * camera was found. 337 | * 338 | * @param facing the desired camera (front-facing or rear-facing) 339 | */ 340 | private static int getIdForRequestedCamera(int facing) { 341 | CameraInfo cameraInfo = new CameraInfo(); 342 | for (int i = 0; i < Camera.getNumberOfCameras(); ++i) { 343 | Camera.getCameraInfo(i, cameraInfo); 344 | if (cameraInfo.facing == facing) { 345 | return i; 346 | } 347 | } 348 | return -1; 349 | } 350 | 351 | /** 352 | * Selects the most suitable preview and picture size, given the desired width and height. 353 | * 354 | *

Even though we only need to find the preview size, it's necessary to find both the preview 355 | * size and the picture size of the camera together, because these need to have the same aspect 356 | * ratio. On some hardware, if you would only set the preview size, you will get a distorted 357 | * image. 358 | * 359 | * @param camera the camera to select a preview size from 360 | * @param desiredWidth the desired width of the camera preview frames 361 | * @param desiredHeight the desired height of the camera preview frames 362 | * @return the selected preview and picture size pair 363 | */ 364 | private static SizePair selectSizePair(Camera camera, int desiredWidth, int desiredHeight) { 365 | List validPreviewSizes = generateValidPreviewSizeList(camera); 366 | 367 | // The method for selecting the best size is to minimize the sum of the differences between 368 | // the desired values and the actual values for width and height. This is certainly not the 369 | // only way to select the best size, but it provides a decent tradeoff between using the 370 | // closest aspect ratio vs. using the closest pixel area. 371 | SizePair selectedPair = null; 372 | int minDiff = Integer.MAX_VALUE; 373 | for (SizePair sizePair : validPreviewSizes) { 374 | Size size = sizePair.previewSize(); 375 | int diff = 376 | Math.abs(size.getWidth() - desiredWidth) + Math.abs(size.getHeight() - desiredHeight); 377 | if (diff < minDiff) { 378 | selectedPair = sizePair; 379 | minDiff = diff; 380 | } 381 | } 382 | 383 | return selectedPair; 384 | } 385 | 386 | /** 387 | * Stores a preview size and a corresponding same-aspect-ratio picture size. To avoid distorted 388 | * preview images on some devices, the picture size must be set to a size that is the same aspect 389 | * ratio as the preview size or the preview may end up being distorted. If the picture size is 390 | * null, then there is no picture size with the same aspect ratio as the preview size. 391 | */ 392 | private static class SizePair { 393 | private final Size preview; 394 | private Size picture; 395 | 396 | SizePair( 397 | android.hardware.Camera.Size previewSize, 398 | @Nullable android.hardware.Camera.Size pictureSize) { 399 | preview = new Size(previewSize.width, previewSize.height); 400 | if (pictureSize != null) { 401 | picture = new Size(pictureSize.width, pictureSize.height); 402 | } 403 | } 404 | 405 | Size previewSize() { 406 | return preview; 407 | } 408 | 409 | @Nullable 410 | Size pictureSize() { 411 | return picture; 412 | } 413 | } 414 | 415 | /** 416 | * Generates a list of acceptable preview sizes. Preview sizes are not acceptable if there is not 417 | * a corresponding picture size of the same aspect ratio. If there is a corresponding picture size 418 | * of the same aspect ratio, the picture size is paired up with the preview size. 419 | * 420 | *

This is necessary because even if we don't use still pictures, the still picture size must 421 | * be set to a size that is the same aspect ratio as the preview size we choose. Otherwise, the 422 | * preview images may be distorted on some devices. 423 | */ 424 | private static List generateValidPreviewSizeList(Camera camera) { 425 | Camera.Parameters parameters = camera.getParameters(); 426 | List supportedPreviewSizes = 427 | parameters.getSupportedPreviewSizes(); 428 | List supportedPictureSizes = 429 | parameters.getSupportedPictureSizes(); 430 | List validPreviewSizes = new ArrayList<>(); 431 | for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) { 432 | float previewAspectRatio = (float) previewSize.width / (float) previewSize.height; 433 | 434 | // By looping through the picture sizes in order, we favor the higher resolutions. 435 | // We choose the highest resolution in order to support taking the full resolution 436 | // picture later. 437 | for (android.hardware.Camera.Size pictureSize : supportedPictureSizes) { 438 | float pictureAspectRatio = (float) pictureSize.width / (float) pictureSize.height; 439 | if (Math.abs(previewAspectRatio - pictureAspectRatio) < ASPECT_RATIO_TOLERANCE) { 440 | validPreviewSizes.add(new SizePair(previewSize, pictureSize)); 441 | break; 442 | } 443 | } 444 | } 445 | 446 | // If there are no picture sizes with the same aspect ratio as any preview sizes, allow all 447 | // of the preview sizes and hope that the camera can handle it. Probably unlikely, but we 448 | // still account for it. 449 | if (validPreviewSizes.size() == 0) { 450 | Log.w(TAG, "No preview sizes have a corresponding same-aspect-ratio picture size"); 451 | for (android.hardware.Camera.Size previewSize : supportedPreviewSizes) { 452 | // The null picture size will let us know that we shouldn't set a picture size. 453 | validPreviewSizes.add(new SizePair(previewSize, null)); 454 | } 455 | } 456 | 457 | return validPreviewSizes; 458 | } 459 | 460 | /** 461 | * Selects the most suitable preview frames per second range, given the desired frames per second. 462 | * 463 | * @param camera the camera to select a frames per second range from 464 | * @param desiredPreviewFps the desired frames per second for the camera preview frames 465 | * @return the selected preview frames per second range 466 | */ 467 | @SuppressLint("InlinedApi") 468 | private static int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) { 469 | // The camera API uses integers scaled by a factor of 1000 instead of floating-point frame 470 | // rates. 471 | int desiredPreviewFpsScaled = (int) (desiredPreviewFps * 1000.0f); 472 | 473 | // The method for selecting the best range is to minimize the sum of the differences between 474 | // the desired value and the upper and lower bounds of the range. This may select a range 475 | // that the desired value is outside of, but this is often preferred. For example, if the 476 | // desired frame rate is 29.97, the range (30, 30) is probably more desirable than the 477 | // range (15, 30). 478 | int[] selectedFpsRange = null; 479 | int minDiff = Integer.MAX_VALUE; 480 | List previewFpsRangeList = camera.getParameters().getSupportedPreviewFpsRange(); 481 | for (int[] range : previewFpsRangeList) { 482 | int deltaMin = desiredPreviewFpsScaled - range[Camera.Parameters.PREVIEW_FPS_MIN_INDEX]; 483 | int deltaMax = desiredPreviewFpsScaled - range[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]; 484 | int diff = Math.abs(deltaMin) + Math.abs(deltaMax); 485 | if (diff < minDiff) { 486 | selectedFpsRange = range; 487 | minDiff = diff; 488 | } 489 | } 490 | return selectedFpsRange; 491 | } 492 | 493 | /** 494 | * Calculates the correct rotation for the given camera id and sets the rotation in the 495 | * parameters. It also sets the camera's display orientation and rotation. 496 | * 497 | * @param parameters the camera parameters for which to set the rotation 498 | * @param cameraId the camera id to set rotation based on 499 | */ 500 | private void setRotation(Camera camera, Camera.Parameters parameters, int cameraId) { 501 | WindowManager windowManager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); 502 | int degrees = 0; 503 | int rotation = windowManager.getDefaultDisplay().getRotation(); 504 | switch (rotation) { 505 | case Surface.ROTATION_0: 506 | degrees = 0; 507 | break; 508 | case Surface.ROTATION_90: 509 | degrees = 90; 510 | break; 511 | case Surface.ROTATION_180: 512 | degrees = 180; 513 | break; 514 | case Surface.ROTATION_270: 515 | degrees = 270; 516 | break; 517 | default: 518 | Log.e(TAG, "Bad rotation value: " + rotation); 519 | } 520 | 521 | CameraInfo cameraInfo = new CameraInfo(); 522 | Camera.getCameraInfo(cameraId, cameraInfo); 523 | 524 | int angle; 525 | int displayAngle; 526 | if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 527 | angle = (cameraInfo.orientation + degrees) % 360; 528 | displayAngle = (360 - angle) % 360; // compensate for it being mirrored 529 | } else { // back-facing 530 | angle = (cameraInfo.orientation - degrees + 360) % 360; 531 | displayAngle = angle; 532 | } 533 | 534 | // This corresponds to the rotation constants. 535 | this.rotation = angle / 90; 536 | 537 | camera.setDisplayOrientation(displayAngle); 538 | parameters.setRotation(angle); 539 | } 540 | 541 | /** 542 | * Creates one buffer for the camera preview callback. The size of the buffer is based off of the 543 | * camera preview size and the format of the camera image. 544 | * 545 | * @return a new preview buffer of the appropriate size for the current camera settings 546 | */ 547 | @SuppressLint("InlinedApi") 548 | private byte[] createPreviewBuffer(Size previewSize) { 549 | int bitsPerPixel = ImageFormat.getBitsPerPixel(ImageFormat.NV21); 550 | long sizeInBits = (long) previewSize.getHeight() * previewSize.getWidth() * bitsPerPixel; 551 | int bufferSize = (int) Math.ceil(sizeInBits / 8.0d) + 1; 552 | 553 | // Creating the byte array this way and wrapping it, as opposed to using .allocate(), 554 | // should guarantee that there will be an array to work with. 555 | byte[] byteArray = new byte[bufferSize]; 556 | ByteBuffer buffer = ByteBuffer.wrap(byteArray); 557 | if (!buffer.hasArray() || (buffer.array() != byteArray)) { 558 | // I don't think that this will ever happen. But if it does, then we wouldn't be 559 | // passing the preview content to the underlying detector later. 560 | throw new IllegalStateException("Failed to create valid buffer for camera source."); 561 | } 562 | 563 | bytesToByteBuffer.put(byteArray, buffer); 564 | return byteArray; 565 | } 566 | 567 | // ============================================================================================== 568 | // Frame processing 569 | // ============================================================================================== 570 | 571 | /** Called when the camera has a new preview frame. */ 572 | private class CameraPreviewCallback implements Camera.PreviewCallback { 573 | @Override 574 | public void onPreviewFrame(byte[] data, Camera camera) { 575 | processingRunnable.setNextFrame(data, camera); 576 | } 577 | } 578 | 579 | void setMachineLearningFrameProcessor(VisionImageProcessor processor) { 580 | synchronized (processorLock) { 581 | cleanScreen(); 582 | if (frameProcessor != null) { 583 | frameProcessor.stop(); 584 | } 585 | frameProcessor = processor; 586 | } 587 | } 588 | 589 | /** 590 | * This runnable controls access to the underlying receiver, calling it to process frames when 591 | * available from the camera. This is designed to run detection on frames as fast as possible 592 | * (i.e., without unnecessary context switching or waiting on the next frame). 593 | * 594 | *

While detection is running on a frame, new frames may be received from the camera. As these 595 | * frames come in, the most recent frame is held onto as pending. As soon as detection and its 596 | * associated processing is done for the previous frame, detection on the mostly recently received 597 | * frame will immediately start on the same thread. 598 | */ 599 | private class FrameProcessingRunnable implements Runnable { 600 | 601 | // This lock guards all of the member variables below. 602 | private final Object lock = new Object(); 603 | private boolean active = true; 604 | 605 | // These pending variables hold the state associated with the new frame awaiting processing. 606 | private ByteBuffer pendingFrameData; 607 | 608 | FrameProcessingRunnable() {} 609 | 610 | /** 611 | * Releases the underlying receiver. This is only safe to do after the associated thread has 612 | * completed, which is managed in camera source's release method above. 613 | */ 614 | @SuppressLint("Assert") 615 | void release() { 616 | assert (processingThread.getState() == State.TERMINATED); 617 | } 618 | 619 | /** Marks the runnable as active/not active. Signals any blocked threads to continue. */ 620 | void setActive(boolean active) { 621 | synchronized (lock) { 622 | this.active = active; 623 | lock.notifyAll(); 624 | } 625 | } 626 | 627 | /** 628 | * Sets the frame data received from the camera. This adds the previous unused frame buffer (if 629 | * present) back to the camera, and keeps a pending reference to the frame data for future use. 630 | */ 631 | void setNextFrame(byte[] data, Camera camera) { 632 | synchronized (lock) { 633 | if (pendingFrameData != null) { 634 | camera.addCallbackBuffer(pendingFrameData.array()); 635 | pendingFrameData = null; 636 | } 637 | 638 | if (!bytesToByteBuffer.containsKey(data)) { 639 | Log.d( 640 | TAG, 641 | "Skipping frame. Could not find ByteBuffer associated with the image " 642 | + "data from the camera."); 643 | return; 644 | } 645 | 646 | pendingFrameData = bytesToByteBuffer.get(data); 647 | 648 | // Notify the processor thread if it is waiting on the next frame (see below). 649 | lock.notifyAll(); 650 | } 651 | } 652 | 653 | /** 654 | * As long as the processing thread is active, this executes detection on frames continuously. 655 | * The next pending frame is either immediately available or hasn't been received yet. Once it 656 | * is available, we transfer the frame info to local variables and run detection on that frame. 657 | * It immediately loops back for the next frame without pausing. 658 | * 659 | *

If detection takes longer than the time in between new frames from the camera, this will 660 | * mean that this loop will run without ever waiting on a frame, avoiding any context switching 661 | * or frame acquisition time latency. 662 | * 663 | *

If you find that this is using more CPU than you'd like, you should probably decrease the 664 | * FPS setting above to allow for some idle time in between frames. 665 | */ 666 | @SuppressLint("InlinedApi") 667 | @SuppressWarnings("GuardedBy") 668 | @Override 669 | public void run() { 670 | ByteBuffer data; 671 | 672 | while (true) { 673 | synchronized (lock) { 674 | while (active && (pendingFrameData == null)) { 675 | try { 676 | // Wait for the next frame to be received from the camera, since we 677 | // don't have it yet. 678 | lock.wait(); 679 | } catch (InterruptedException e) { 680 | Log.d(TAG, "Frame processing loop terminated.", e); 681 | return; 682 | } 683 | } 684 | 685 | if (!active) { 686 | // Exit the loop once this camera source is stopped or released. We check 687 | // this here, immediately after the wait() above, to handle the case where 688 | // setActive(false) had been called, triggering the termination of this 689 | // loop. 690 | return; 691 | } 692 | 693 | // Hold onto the frame data locally, so that we can use this for detection 694 | // below. We need to clear pendingFrameData to ensure that this buffer isn't 695 | // recycled back to the camera before we are done using that data. 696 | data = pendingFrameData; 697 | pendingFrameData = null; 698 | } 699 | 700 | // The code below needs to run outside of synchronization, because this will allow 701 | // the camera to add pending frame(s) while we are running detection on the current 702 | // frame. 703 | 704 | try { 705 | synchronized (processorLock) { 706 | Log.d(TAG, "Process an image"); 707 | frameProcessor.process( 708 | data, 709 | new FrameMetadata.Builder() 710 | .setWidth(previewSize.getWidth()) 711 | .setHeight(previewSize.getHeight()) 712 | .setRotation(rotation) 713 | .setCameraFacing(facing) 714 | .build(), 715 | graphicOverlay); 716 | } 717 | } catch (Throwable t) { 718 | Log.e(TAG, "Exception thrown from receiver.", t); 719 | } finally { 720 | camera.addCallbackBuffer(data.array()); 721 | } 722 | } 723 | } 724 | } 725 | 726 | /** Cleans up graphicOverlay and child classes can do their cleanups as well . */ 727 | private void cleanScreen() { 728 | graphicOverlay.clear(); 729 | } 730 | } 731 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/CameraSourcePreview.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.content.Context; 17 | import android.content.res.Configuration; 18 | import android.util.AttributeSet; 19 | import android.util.Log; 20 | import android.view.SurfaceHolder; 21 | import android.view.SurfaceView; 22 | import android.view.ViewGroup; 23 | 24 | import com.google.android.gms.common.images.Size; 25 | 26 | import java.io.IOException; 27 | 28 | /** Preview the camera image in the screen. */ 29 | public class CameraSourcePreview extends ViewGroup { 30 | private static final String TAG = "MIDemoApp:Preview"; 31 | 32 | private Context context; 33 | private SurfaceView surfaceView; 34 | private boolean startRequested; 35 | private boolean surfaceAvailable; 36 | private CameraSource cameraSource; 37 | 38 | private GraphicOverlay overlay; 39 | 40 | public CameraSourcePreview(Context context, AttributeSet attrs) { 41 | super(context, attrs); 42 | this.context = context; 43 | startRequested = false; 44 | surfaceAvailable = false; 45 | 46 | surfaceView = new SurfaceView(context); 47 | surfaceView.getHolder().addCallback(new SurfaceCallback()); 48 | addView(surfaceView); 49 | } 50 | 51 | public void start(CameraSource cameraSource) throws IOException { 52 | if (cameraSource == null) { 53 | stop(); 54 | } 55 | 56 | this.cameraSource = cameraSource; 57 | 58 | if (this.cameraSource != null) { 59 | startRequested = true; 60 | startIfReady(); 61 | } 62 | } 63 | 64 | public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException { 65 | this.overlay = overlay; 66 | start(cameraSource); 67 | } 68 | 69 | public void stop() { 70 | if (cameraSource != null) { 71 | cameraSource.stop(); 72 | } 73 | } 74 | 75 | public void release() { 76 | if (cameraSource != null) { 77 | cameraSource.release(); 78 | cameraSource = null; 79 | } 80 | } 81 | 82 | private void startIfReady() throws IOException { 83 | if (startRequested && surfaceAvailable) { 84 | cameraSource.start(surfaceView.getHolder()); 85 | if (overlay != null) { 86 | Size size = cameraSource.getPreviewSize(); 87 | int min = Math.min(size.getWidth(), size.getHeight()); 88 | int max = Math.max(size.getWidth(), size.getHeight()); 89 | if (isPortraitMode()) { 90 | // Swap width and height sizes when in portrait, since it will be rotated by 91 | // 90 degrees 92 | overlay.setCameraInfo(min, max, cameraSource.getCameraFacing()); 93 | } else { 94 | overlay.setCameraInfo(max, min, cameraSource.getCameraFacing()); 95 | } 96 | overlay.clear(); 97 | } 98 | startRequested = false; 99 | } 100 | } 101 | 102 | private class SurfaceCallback implements SurfaceHolder.Callback { 103 | @Override 104 | public void surfaceCreated(SurfaceHolder surface) { 105 | surfaceAvailable = true; 106 | try { 107 | startIfReady(); 108 | } catch (IOException e) { 109 | Log.e(TAG, "Could not start camera source.", e); 110 | } 111 | } 112 | 113 | @Override 114 | public void surfaceDestroyed(SurfaceHolder surface) { 115 | surfaceAvailable = false; 116 | } 117 | 118 | @Override 119 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} 120 | } 121 | 122 | @Override 123 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 124 | int width = 320; 125 | int height = 240; 126 | if (cameraSource != null) { 127 | Size size = cameraSource.getPreviewSize(); 128 | if (size != null) { 129 | width = size.getWidth(); 130 | height = size.getHeight(); 131 | } 132 | } 133 | 134 | // Swap width and height sizes when in portrait, since it will be rotated 90 degrees 135 | if (isPortraitMode()) { 136 | int tmp = width; 137 | width = height; 138 | height = tmp; 139 | } 140 | 141 | final int layoutWidth = right - left; 142 | final int layoutHeight = bottom - top; 143 | 144 | // Computes height and width for potentially doing fit width. 145 | int childWidth = layoutWidth; 146 | int childHeight = (int) (((float) layoutWidth / (float) width) * height); 147 | 148 | // If height is too tall using fit width, does fit height instead. 149 | if (childHeight > layoutHeight) { 150 | childHeight = layoutHeight; 151 | childWidth = (int) (((float) layoutHeight / (float) height) * width); 152 | } 153 | 154 | for (int i = 0; i < getChildCount(); ++i) { 155 | getChildAt(i).layout(0, 0, childWidth, childHeight); 156 | Log.d(TAG, "Assigned view: " + i); 157 | } 158 | 159 | try { 160 | startIfReady(); 161 | } catch (IOException e) { 162 | Log.e(TAG, "Could not start camera source.", e); 163 | } 164 | } 165 | 166 | private boolean isPortraitMode() { 167 | int orientation = context.getResources().getConfiguration().orientation; 168 | if (orientation == Configuration.ORIENTATION_LANDSCAPE) { 169 | return false; 170 | } 171 | if (orientation == Configuration.ORIENTATION_PORTRAIT) { 172 | return true; 173 | } 174 | 175 | Log.d(TAG, "isPortraitMode returning false by default"); 176 | return false; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/ChooserActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.content.Context; 17 | import android.content.Intent; 18 | import android.content.pm.PackageInfo; 19 | import android.content.pm.PackageManager; 20 | import android.os.Bundle; 21 | import android.support.v4.app.ActivityCompat; 22 | import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback; 23 | import android.support.v4.content.ContextCompat; 24 | import android.support.v7.app.AppCompatActivity; 25 | import android.util.Log; 26 | import android.view.LayoutInflater; 27 | import android.view.View; 28 | import android.view.ViewGroup; 29 | import android.widget.AdapterView; 30 | import android.widget.ArrayAdapter; 31 | import android.widget.Button; 32 | import android.widget.ListView; 33 | import android.widget.TextView; 34 | 35 | import java.util.ArrayList; 36 | import java.util.List; 37 | 38 | /** 39 | * Demo app chooser which takes care of runtime permission requesting and allows you to pick from 40 | * all available testing Activities. 41 | */ 42 | public final class ChooserActivity extends AppCompatActivity 43 | implements OnRequestPermissionsResultCallback, AdapterView.OnItemClickListener { 44 | private static final String TAG = "ChooserActivity"; 45 | private static final int PERMISSION_REQUESTS = 1; 46 | public Button startButton; 47 | 48 | private static final Class[] CLASSES = 49 | new Class[] { 50 | LivePreviewActivity.class//, StillImageActivity.class, 51 | }; 52 | 53 | private static final int[] DESCRIPTION_IDS = 54 | new int[] { 55 | R.string.desc_camera_source_activity, R.string.desc_still_image_activity, 56 | }; 57 | 58 | @Override 59 | protected void onCreate(Bundle savedInstanceState) { 60 | super.onCreate(savedInstanceState); 61 | Log.d(TAG, "onCreate"); 62 | 63 | setContentView(R.layout.activity_chooser); 64 | 65 | startButton = (Button) findViewById(R.id.start); 66 | 67 | startButton.setOnClickListener(new View.OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | Class clicked = CLASSES[0]; 71 | startActivity(new Intent( getApplicationContext(), clicked)); 72 | } 73 | }); 74 | 75 | // Set up ListView and Adapter 76 | ListView listView = (ListView) findViewById(R.id.test_activity_list_view); 77 | 78 | MyArrayAdapter adapter = new MyArrayAdapter(this, android.R.layout.simple_list_item_2, CLASSES); 79 | adapter.setDescriptionIds(DESCRIPTION_IDS); 80 | 81 | listView.setAdapter(adapter); 82 | listView.setOnItemClickListener(this); 83 | 84 | if (!allPermissionsGranted()) { 85 | getRuntimePermissions(); 86 | } 87 | } 88 | 89 | @Override 90 | public void onItemClick(AdapterView parent, View view, int position, long id) { 91 | Class clicked = CLASSES[position]; 92 | startActivity(new Intent(this, clicked)); 93 | } 94 | 95 | private String[] getRequiredPermissions() { 96 | try { 97 | PackageInfo info = 98 | this.getPackageManager() 99 | .getPackageInfo(this.getPackageName(), PackageManager.GET_PERMISSIONS); 100 | String[] ps = info.requestedPermissions; 101 | if (ps != null && ps.length > 0) { 102 | return ps; 103 | } else { 104 | return new String[0]; 105 | } 106 | } catch (Exception e) { 107 | return new String[0]; 108 | } 109 | } 110 | 111 | private boolean allPermissionsGranted() { 112 | for (String permission : getRequiredPermissions()) { 113 | if (!isPermissionGranted(this, permission)) { 114 | return false; 115 | } 116 | } 117 | return true; 118 | } 119 | 120 | private void getRuntimePermissions() { 121 | List allNeededPermissions = new ArrayList<>(); 122 | for (String permission : getRequiredPermissions()) { 123 | if (!isPermissionGranted(this, permission)) { 124 | allNeededPermissions.add(permission); 125 | } 126 | } 127 | 128 | if (!allNeededPermissions.isEmpty()) { 129 | ActivityCompat.requestPermissions( 130 | this, allNeededPermissions.toArray(new String[0]), PERMISSION_REQUESTS); 131 | } 132 | } 133 | 134 | private static boolean isPermissionGranted(Context context, String permission) { 135 | if (ContextCompat.checkSelfPermission(context, permission) 136 | == PackageManager.PERMISSION_GRANTED) { 137 | Log.i(TAG, "Permission granted: " + permission); 138 | return true; 139 | } 140 | Log.i(TAG, "Permission NOT granted: " + permission); 141 | return false; 142 | } 143 | 144 | private static class MyArrayAdapter extends ArrayAdapter> { 145 | 146 | private final Context context; 147 | private final Class[] classes; 148 | private int[] descriptionIds; 149 | 150 | public MyArrayAdapter(Context context, int resource, Class[] objects) { 151 | super(context, resource, objects); 152 | 153 | this.context = context; 154 | classes = objects; 155 | } 156 | 157 | @Override 158 | public View getView(int position, View convertView, ViewGroup parent) { 159 | View view = convertView; 160 | 161 | if (convertView == null) { 162 | LayoutInflater inflater = 163 | (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE); 164 | view = inflater.inflate(android.R.layout.simple_list_item_2, null); 165 | } 166 | 167 | // ((TextView) view.findViewById(android.R.id.text1)).setText(classes[position].getSimpleName()); 168 | ((TextView) view.findViewById(android.R.id.text1)).setText("START"); 169 | ((TextView) view.findViewById(android.R.id.text2)).setText(descriptionIds[position]); 170 | 171 | return view; 172 | } 173 | 174 | public void setDescriptionIds(int[] descriptionIds) { 175 | this.descriptionIds = descriptionIds; 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/FrameMetadata.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | /** Describing a frame info. */ 17 | public class FrameMetadata { 18 | 19 | private final int width; 20 | private final int height; 21 | private final int rotation; 22 | private final int cameraFacing; 23 | 24 | public int getWidth() { 25 | return width; 26 | } 27 | 28 | public int getHeight() { 29 | return height; 30 | } 31 | 32 | public int getRotation() { 33 | return rotation; 34 | } 35 | 36 | public int getCameraFacing() { 37 | return cameraFacing; 38 | } 39 | 40 | private FrameMetadata(int width, int height, int rotation, int facing) { 41 | this.width = width; 42 | this.height = height; 43 | this.rotation = rotation; 44 | cameraFacing = facing; 45 | } 46 | 47 | /** Builder of {@link FrameMetadata}. */ 48 | public static class Builder { 49 | 50 | private int width; 51 | private int height; 52 | private int rotation; 53 | private int cameraFacing; 54 | 55 | public Builder setWidth(int width) { 56 | this.width = width; 57 | return this; 58 | } 59 | 60 | public Builder setHeight(int height) { 61 | this.height = height; 62 | return this; 63 | } 64 | 65 | public Builder setRotation(int rotation) { 66 | this.rotation = rotation; 67 | return this; 68 | } 69 | 70 | public Builder setCameraFacing(int facing) { 71 | cameraFacing = facing; 72 | return this; 73 | } 74 | 75 | public FrameMetadata build() { 76 | return new FrameMetadata(width, height, rotation, cameraFacing); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/GraphicOverlay.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.content.Context; 17 | import android.graphics.Canvas; 18 | import android.hardware.camera2.CameraCharacteristics; 19 | import android.util.AttributeSet; 20 | import android.view.View; 21 | 22 | import com.google.android.gms.vision.CameraSource; 23 | 24 | import java.util.HashSet; 25 | import java.util.Set; 26 | 27 | /** 28 | * A view which renders a series of custom graphics to be overlayed on top of an associated preview 29 | * (i.e., the camera preview). The creator can add graphics objects, update the objects, and remove 30 | * them, triggering the appropriate drawing and invalidation within the view. 31 | * 32 | *

Supports scaling and mirroring of the graphics relative the camera's preview properties. The 33 | * idea is that detection items are expressed in terms of a preview size, but need to be scaled up 34 | * to the full view size, and also mirrored in the case of the front-facing camera. 35 | * 36 | *

Associated {@link Graphic} items should use the following methods to convert to view 37 | * coordinates for the graphics that are drawn: 38 | * 39 | *

    40 | *
  1. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the 41 | * supplied value from the preview scale to the view scale. 42 | *
  2. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the 43 | * coordinate from the preview's coordinate system to the view coordinate system. 44 | *
45 | */ 46 | public class GraphicOverlay extends View { 47 | private final Object lock = new Object(); 48 | private int previewWidth; 49 | private float widthScaleFactor = 1.0f; 50 | private int previewHeight; 51 | private float heightScaleFactor = 1.0f; 52 | private int facing = CameraSource.CAMERA_FACING_BACK; 53 | private Set graphics = new HashSet<>(); 54 | 55 | // private int mRatioWidth = 0; 56 | // private int mRatioHeight = 0; 57 | 58 | /** 59 | * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass 60 | * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add 61 | * instances to the overlay using {@link GraphicOverlay#add(Graphic)}. 62 | */ 63 | public abstract static class Graphic { 64 | private GraphicOverlay overlay; 65 | 66 | public Graphic(GraphicOverlay overlay) { 67 | this.overlay = overlay; 68 | } 69 | 70 | /** 71 | * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert 72 | * to view coordinates for the graphics that are drawn: 73 | * 74 | *
    75 | *
  1. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the 76 | * supplied value from the preview scale to the view scale. 77 | *
  2. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the 78 | * coordinate from the preview's coordinate system to the view coordinate system. 79 | *
80 | * 81 | * @param canvas drawing canvas 82 | */ 83 | public abstract void draw(Canvas canvas); 84 | 85 | /** 86 | * Adjusts a horizontal value of the supplied value from the preview scale to the view scale. 87 | */ 88 | public float scaleX(float horizontal) { 89 | return horizontal * overlay.widthScaleFactor; 90 | } 91 | 92 | /** Adjusts a vertical value of the supplied value from the preview scale to the view scale. */ 93 | public float scaleY(float vertical) { 94 | return vertical * overlay.heightScaleFactor; 95 | } 96 | 97 | /** Returns the application context of the app. */ 98 | public Context getApplicationContext() { 99 | return overlay.getContext().getApplicationContext(); 100 | } 101 | 102 | /** 103 | * Adjusts the x coordinate from the preview's coordinate system to the view coordinate system. 104 | */ 105 | public float translateX(float x) { 106 | if (overlay.facing == CameraSource.CAMERA_FACING_FRONT) { 107 | return overlay.getWidth() - scaleX(x); 108 | } else { 109 | return scaleX(x); 110 | } 111 | } 112 | 113 | /** 114 | * Adjusts the y coordinate from the preview's coordinate system to the view coordinate system. 115 | */ 116 | public float translateY(float y) { 117 | return scaleY(y); 118 | } 119 | 120 | public void postInvalidate() { 121 | overlay.postInvalidate(); 122 | } 123 | } 124 | 125 | public GraphicOverlay(Context context, AttributeSet attrs) { 126 | super(context, attrs); 127 | } 128 | 129 | /** Removes all graphics from the overlay. */ 130 | public void clear() { 131 | synchronized (lock) { 132 | graphics.clear(); 133 | } 134 | postInvalidate(); 135 | } 136 | 137 | /** Adds a graphic to the overlay. */ 138 | public void add(Graphic graphic) { 139 | synchronized (lock) { 140 | graphics.add(graphic); 141 | } 142 | postInvalidate(); 143 | } 144 | 145 | /** Removes a graphic from the overlay. */ 146 | public void remove(Graphic graphic) { 147 | synchronized (lock) { 148 | graphics.remove(graphic); 149 | } 150 | postInvalidate(); 151 | } 152 | 153 | /** 154 | * Sets the camera attributes for size and facing direction, which informs how to transform image 155 | * coordinates later. 156 | */ 157 | public void setCameraInfo(int previewWidth, int previewHeight, int facing) { 158 | synchronized (lock) { 159 | this.previewWidth = previewWidth; 160 | this.previewHeight = previewHeight; 161 | this.facing = facing; 162 | } 163 | postInvalidate(); 164 | } 165 | 166 | /** Draws the overlay with its associated graphic objects. */ 167 | @Override 168 | protected void onDraw(Canvas canvas) { 169 | super.onDraw(canvas); 170 | 171 | synchronized (lock) { 172 | if ((previewWidth != 0) && (previewHeight != 0)) { 173 | widthScaleFactor = (float) canvas.getWidth() / (float) previewWidth; 174 | heightScaleFactor = (float) canvas.getHeight() / (float) previewHeight; 175 | } 176 | 177 | for (Graphic graphic : graphics) { 178 | graphic.draw(canvas); 179 | } 180 | } 181 | } 182 | 183 | // /** 184 | // * Sets the aspect ratio for this view. The size of the view will be measured based on the ratio 185 | // * calculated from the parameters. Note that the actual sizes of parameters don't matter, that 186 | // * is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result. 187 | // * 188 | // * @param width Relative horizontal size 189 | // * @param height Relative vertical size 190 | // */ 191 | // public void setAspectRatio(int width, int height) { 192 | // if (width < 0 || height < 0) { 193 | // throw new IllegalArgumentException("Size cannot be negative."); 194 | // } 195 | // mRatioWidth = width; 196 | // mRatioHeight = height; 197 | // requestLayout(); 198 | // } 199 | // 200 | // @Override 201 | // protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 202 | // super.onMeasure(widthMeasureSpec, heightMeasureSpec); 203 | // int width = MeasureSpec.getSize(widthMeasureSpec); 204 | // int height = MeasureSpec.getSize(heightMeasureSpec); 205 | // if (0 == mRatioWidth || 0 == mRatioHeight) { 206 | // setMeasuredDimension(width, height); 207 | // } else { 208 | // if (width < height * mRatioWidth / mRatioHeight) { 209 | // setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); 210 | // } else { 211 | // setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); 212 | // } 213 | // } 214 | // } 215 | } 216 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/LivePreviewActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.content.Context; 17 | import android.content.pm.PackageInfo; 18 | import android.content.pm.PackageManager; 19 | import android.os.Bundle; 20 | import android.support.v4.app.ActivityCompat; 21 | import android.support.v4.app.ActivityCompat.OnRequestPermissionsResultCallback; 22 | import android.support.v4.content.ContextCompat; 23 | import android.support.v7.app.AppCompatActivity; 24 | import android.util.Log; 25 | import android.view.View; 26 | import android.widget.AdapterView; 27 | import android.widget.AdapterView.OnItemSelectedListener; 28 | import android.widget.ArrayAdapter; 29 | import android.widget.CompoundButton; 30 | import android.widget.Spinner; 31 | import android.widget.ToggleButton; 32 | 33 | import com.google.android.gms.common.annotation.KeepName; 34 | import com.google.firebase.ml.common.FirebaseMLException; 35 | import com.google.firebase.samples.apps.mlkit.barcodescanning.BarcodeScanningProcessor; 36 | import com.google.firebase.samples.apps.mlkit.custommodel.CustomImageClassifierProcessor; 37 | import com.google.firebase.samples.apps.mlkit.facedetection.FaceDetectionProcessor; 38 | import com.google.firebase.samples.apps.mlkit.imagelabeling.ImageLabelingProcessor; 39 | import com.google.firebase.samples.apps.mlkit.textrecognition.TextRecognitionProcessor; 40 | 41 | import java.io.IOException; 42 | import java.util.ArrayList; 43 | import java.util.List; 44 | 45 | /** Demo app showing the various features of ML Kit for Firebase. This class is used to 46 | * set up continuous frame processing on frames from a camera source. */ 47 | @KeepName 48 | public final class LivePreviewActivity extends AppCompatActivity 49 | implements OnRequestPermissionsResultCallback, 50 | OnItemSelectedListener, 51 | CompoundButton.OnCheckedChangeListener { 52 | private static final String FACE_DETECTION = "Face Detection"; 53 | private static final String TEXT_DETECTION = "Text Detection"; 54 | private static final String BARCODE_DETECTION = "Barcode Detection"; 55 | private static final String IMAGE_LABEL_DETECTION = "Label Detection"; 56 | private static final String CLASSIFICATION = "Classification"; 57 | private static final String TAG = "LivePreviewActivity"; 58 | private static final int PERMISSION_REQUESTS = 1; 59 | 60 | private CameraSource cameraSource = null; 61 | private CameraSourcePreview preview; 62 | private GraphicOverlay graphicOverlay; 63 | private String selectedModel = FACE_DETECTION; 64 | 65 | @Override 66 | protected void onCreate(Bundle savedInstanceState) { 67 | super.onCreate(savedInstanceState); 68 | Log.d(TAG, "onCreate"); 69 | 70 | setContentView(R.layout.activity_live_preview); 71 | 72 | preview = (CameraSourcePreview) findViewById(R.id.firePreview); 73 | if (preview == null) { 74 | Log.d(TAG, "Preview is null"); 75 | } 76 | graphicOverlay = (GraphicOverlay) findViewById(R.id.fireFaceOverlay); 77 | if (graphicOverlay == null) { 78 | Log.d(TAG, "graphicOverlay is null"); 79 | } 80 | 81 | Spinner spinner = (Spinner) findViewById(R.id.spinner); 82 | List options = new ArrayList<>(); 83 | options.add(FACE_DETECTION); 84 | options.add(TEXT_DETECTION); 85 | options.add(BARCODE_DETECTION); 86 | options.add(IMAGE_LABEL_DETECTION); 87 | options.add(CLASSIFICATION); 88 | // Creating adapter for spinner 89 | ArrayAdapter dataAdapter = new ArrayAdapter<>(this, R.layout.spinner_style, options); 90 | // Drop down layout style - list view with radio button 91 | dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 92 | // attaching data adapter to spinner 93 | spinner.setAdapter(dataAdapter); 94 | spinner.setOnItemSelectedListener(this); 95 | 96 | ToggleButton facingSwitch = (ToggleButton) findViewById(R.id.facingswitch); 97 | facingSwitch.setOnCheckedChangeListener(this); 98 | 99 | if (allPermissionsGranted()) { 100 | createCameraSource(selectedModel); 101 | } else { 102 | getRuntimePermissions(); 103 | } 104 | } 105 | 106 | @Override 107 | public synchronized void onItemSelected(AdapterView parent, View view, int pos, long id) { 108 | // An item was selected. You can retrieve the selected item using 109 | // parent.getItemAtPosition(pos) 110 | selectedModel = parent.getItemAtPosition(pos).toString(); 111 | Log.d(TAG, "Selected model: " + selectedModel); 112 | preview.stop(); 113 | if (allPermissionsGranted()) { 114 | createCameraSource(selectedModel); 115 | startCameraSource(); 116 | } else { 117 | getRuntimePermissions(); 118 | } 119 | } 120 | 121 | @Override 122 | public void onNothingSelected(AdapterView parent) { 123 | // Do nothing. 124 | } 125 | 126 | @Override 127 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 128 | Log.d(TAG, "Set facing"); 129 | if (cameraSource != null) { 130 | if (isChecked) { 131 | cameraSource.setFacing(CameraSource.CAMERA_FACING_FRONT); 132 | } else { 133 | cameraSource.setFacing(CameraSource.CAMERA_FACING_BACK); 134 | } 135 | } 136 | preview.stop(); 137 | startCameraSource(); 138 | } 139 | 140 | private void createCameraSource(String model) { 141 | // If there's no existing cameraSource, create one. 142 | if (cameraSource == null) { 143 | cameraSource = new CameraSource(this, graphicOverlay); 144 | } 145 | 146 | try { 147 | switch (model) { 148 | case CLASSIFICATION: 149 | Log.i(TAG, "Using Custom Image Classifier Processor"); 150 | cameraSource.setMachineLearningFrameProcessor(new CustomImageClassifierProcessor(this)); 151 | break; 152 | case TEXT_DETECTION: 153 | Log.i(TAG, "Using Text Detector Processor"); 154 | cameraSource.setMachineLearningFrameProcessor(new TextRecognitionProcessor()); 155 | break; 156 | case FACE_DETECTION: 157 | Log.i(TAG, "Using Face Detector Processor"); 158 | cameraSource.setMachineLearningFrameProcessor(new FaceDetectionProcessor()); 159 | break; 160 | case BARCODE_DETECTION: 161 | Log.i(TAG, "Using Barcode Detector Processor"); 162 | cameraSource.setMachineLearningFrameProcessor(new BarcodeScanningProcessor()); 163 | break; 164 | case IMAGE_LABEL_DETECTION: 165 | Log.i(TAG, "Using Image Label Detector Processor"); 166 | cameraSource.setMachineLearningFrameProcessor(new ImageLabelingProcessor()); 167 | break; 168 | default: 169 | Log.e(TAG, "Unknown model: " + model); 170 | } 171 | } catch (FirebaseMLException e) { 172 | Log.e(TAG, "can not create camera source: " + model); 173 | } 174 | } 175 | 176 | /** 177 | * Starts or restarts the camera source, if it exists. If the camera source doesn't exist yet 178 | * (e.g., because onResume was called before the camera source was created), this will be called 179 | * again when the camera source is created. 180 | */ 181 | private void startCameraSource() { 182 | if (cameraSource != null) { 183 | try { 184 | if (preview == null) { 185 | Log.d(TAG, "resume: Preview is null"); 186 | } 187 | if (graphicOverlay == null) { 188 | Log.d(TAG, "resume: graphOverlay is null"); 189 | } 190 | preview.start(cameraSource, graphicOverlay); 191 | } catch (IOException e) { 192 | Log.e(TAG, "Unable to start camera source.", e); 193 | cameraSource.release(); 194 | cameraSource = null; 195 | } 196 | } 197 | } 198 | 199 | @Override 200 | public void onResume() { 201 | super.onResume(); 202 | Log.d(TAG, "onResume"); 203 | startCameraSource(); 204 | } 205 | 206 | /** Stops the camera. */ 207 | @Override 208 | protected void onPause() { 209 | super.onPause(); 210 | preview.stop(); 211 | } 212 | 213 | @Override 214 | public void onDestroy() { 215 | super.onDestroy(); 216 | if (cameraSource != null) { 217 | cameraSource.release(); 218 | } 219 | } 220 | 221 | private String[] getRequiredPermissions() { 222 | try { 223 | PackageInfo info = 224 | this.getPackageManager() 225 | .getPackageInfo(this.getPackageName(), PackageManager.GET_PERMISSIONS); 226 | String[] ps = info.requestedPermissions; 227 | if (ps != null && ps.length > 0) { 228 | return ps; 229 | } else { 230 | return new String[0]; 231 | } 232 | } catch (Exception e) { 233 | return new String[0]; 234 | } 235 | } 236 | 237 | private boolean allPermissionsGranted() { 238 | for (String permission : getRequiredPermissions()) { 239 | if (!isPermissionGranted(this, permission)) { 240 | return false; 241 | } 242 | } 243 | return true; 244 | } 245 | 246 | private void getRuntimePermissions() { 247 | List allNeededPermissions = new ArrayList<>(); 248 | for (String permission : getRequiredPermissions()) { 249 | if (!isPermissionGranted(this, permission)) { 250 | allNeededPermissions.add(permission); 251 | } 252 | } 253 | 254 | if (!allNeededPermissions.isEmpty()) { 255 | ActivityCompat.requestPermissions( 256 | this, allNeededPermissions.toArray(new String[0]), PERMISSION_REQUESTS); 257 | } 258 | } 259 | 260 | @Override 261 | public void onRequestPermissionsResult( 262 | int requestCode, String[] permissions, int[] grantResults) { 263 | Log.i(TAG, "Permission granted!"); 264 | if (allPermissionsGranted()) { 265 | createCameraSource(selectedModel); 266 | } 267 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 268 | } 269 | 270 | private static boolean isPermissionGranted(Context context, String permission) { 271 | if (ContextCompat.checkSelfPermission(context, permission) 272 | == PackageManager.PERMISSION_GRANTED) { 273 | Log.i(TAG, "Permission granted: " + permission); 274 | return true; 275 | } 276 | Log.i(TAG, "Permission NOT granted: " + permission); 277 | return false; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/StillImageActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.content.ContentValues; 17 | import android.content.Intent; 18 | import android.content.res.Configuration; 19 | import android.graphics.Bitmap; 20 | import android.net.Uri; 21 | import android.os.Bundle; 22 | import android.provider.MediaStore; 23 | import android.support.v7.app.AppCompatActivity; 24 | import android.util.Log; 25 | import android.util.Pair; 26 | import android.view.MenuInflater; 27 | import android.view.MenuItem; 28 | import android.view.View; 29 | import android.view.View.OnClickListener; 30 | import android.widget.AdapterView; 31 | import android.widget.AdapterView.OnItemSelectedListener; 32 | import android.widget.ArrayAdapter; 33 | import android.widget.Button; 34 | import android.widget.ImageView; 35 | import android.widget.PopupMenu; 36 | import android.widget.PopupMenu.OnMenuItemClickListener; 37 | import android.widget.Spinner; 38 | 39 | import com.google.android.gms.common.annotation.KeepName; 40 | import com.google.firebase.samples.apps.mlkit.cloudimagelabeling.CloudImageLabelingProcessor; 41 | import com.google.firebase.samples.apps.mlkit.cloudlandmarkrecognition.CloudLandmarkRecognitionProcessor; 42 | import com.google.firebase.samples.apps.mlkit.cloudtextrecognition.CloudDocumentTextRecognitionProcessor; 43 | 44 | 45 | import com.google.firebase.samples.apps.mlkit.cloudtextrecognition.CloudTextRecognitionProcessor; 46 | 47 | import java.io.IOException; 48 | import java.util.ArrayList; 49 | import java.util.List; 50 | 51 | /** Activity demonstrating different image detector features with a still image from camera. */ 52 | @KeepName 53 | public final class StillImageActivity extends AppCompatActivity { 54 | 55 | private static final String TAG = "StillImageActivity"; 56 | 57 | private static final String CLOUD_LABEL_DETECTION = "Cloud Label"; 58 | private static final String CLOUD_LANDMARK_DETECTION = "Landmark"; 59 | private static final String CLOUD_TEXT_DETECTION = "Cloud Text"; 60 | private static final String CLOUD_DOCUMENT_TEXT_DETECTION = "Doc Text"; 61 | 62 | private static final String SIZE_PREVIEW = "w:max"; // Available on-screen width. 63 | private static final String SIZE_1024_768 = "w:1024"; // ~1024*768 in a normal ratio 64 | private static final String SIZE_640_480 = "w:640"; // ~640*480 in a normal ratio 65 | 66 | private static final String KEY_IMAGE_URI = "com.googletest.firebase.ml.demo.KEY_IMAGE_URI"; 67 | private static final String KEY_IMAGE_MAX_WIDTH = 68 | "com.googletest.firebase.ml.demo.KEY_IMAGE_MAX_WIDTH"; 69 | private static final String KEY_IMAGE_MAX_HEIGHT = 70 | "com.googletest.firebase.ml.demo.KEY_IMAGE_MAX_HEIGHT"; 71 | private static final String KEY_SELECTED_SIZE = 72 | "com.googletest.firebase.ml.demo.KEY_SELECTED_SIZE"; 73 | 74 | private static final int REQUEST_IMAGE_CAPTURE = 1001; 75 | private static final int REQUEST_CHOOSE_IMAGE = 1002; 76 | 77 | private Button getImageButton; 78 | private ImageView preview; 79 | private GraphicOverlay graphicOverlay; 80 | private String selectedMode = CLOUD_LABEL_DETECTION; 81 | private String selectedSize = SIZE_PREVIEW; 82 | 83 | boolean isLandScape; 84 | 85 | private Uri imageUri; 86 | // Max width (portrait mode) 87 | private Integer imageMaxWidth; 88 | // Max height (portrait mode) 89 | private Integer imageMaxHeight; 90 | private Bitmap bitmapForDetection; 91 | private VisionImageProcessor imageProcessor; 92 | 93 | @Override 94 | protected void onCreate(Bundle savedInstanceState) { 95 | super.onCreate(savedInstanceState); 96 | 97 | setContentView(R.layout.activity_still_image); 98 | 99 | getImageButton = (Button) findViewById(R.id.getImageButton); 100 | getImageButton.setOnClickListener( 101 | new OnClickListener() { 102 | @Override 103 | public void onClick(View view) { 104 | // Menu for selecting either: a) take new photo b) select from existing 105 | PopupMenu popup = new PopupMenu(StillImageActivity.this, view); 106 | popup.setOnMenuItemClickListener( 107 | new OnMenuItemClickListener() { 108 | @Override 109 | public boolean onMenuItemClick(MenuItem menuItem) { 110 | switch (menuItem.getItemId()) { 111 | case R.id.select_images_from_local: 112 | startChooseImageIntentForResult(); 113 | return true; 114 | case R.id.take_photo_using_camera: 115 | startCameraIntentForResult(); 116 | return true; 117 | default: 118 | return false; 119 | } 120 | } 121 | }); 122 | 123 | MenuInflater inflater = popup.getMenuInflater(); 124 | inflater.inflate(R.menu.camera_button_menu, popup.getMenu()); 125 | popup.show(); 126 | } 127 | }); 128 | preview = (ImageView) findViewById(R.id.previewPane); 129 | if (preview == null) { 130 | Log.d(TAG, "Preview is null"); 131 | } 132 | graphicOverlay = (GraphicOverlay) findViewById(R.id.previewOverlay); 133 | if (graphicOverlay == null) { 134 | Log.d(TAG, "graphicOverlay is null"); 135 | } 136 | 137 | populateFeatureSelector(); 138 | populateSizeSelector(); 139 | 140 | createImageProcessor(); 141 | 142 | isLandScape = 143 | (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE); 144 | 145 | if (savedInstanceState != null) { 146 | imageUri = savedInstanceState.getParcelable(KEY_IMAGE_URI); 147 | imageMaxWidth = savedInstanceState.getInt(KEY_IMAGE_MAX_WIDTH); 148 | imageMaxHeight = savedInstanceState.getInt(KEY_IMAGE_MAX_HEIGHT); 149 | selectedSize = savedInstanceState.getString(KEY_SELECTED_SIZE); 150 | 151 | if (imageUri != null) { 152 | tryReloadAndDetectInImage(); 153 | } 154 | } 155 | } 156 | 157 | private void populateFeatureSelector() { 158 | Spinner featureSpinner = (Spinner) findViewById(R.id.featureSelector); 159 | List options = new ArrayList<>(); 160 | options.add(CLOUD_LABEL_DETECTION); 161 | options.add(CLOUD_LANDMARK_DETECTION); 162 | options.add(CLOUD_TEXT_DETECTION); 163 | options.add(CLOUD_DOCUMENT_TEXT_DETECTION); 164 | // Creating adapter for featureSpinner 165 | ArrayAdapter dataAdapter = new ArrayAdapter<>(this, R.layout.spinner_style, options); 166 | // Drop down layout style - list view with radio button 167 | dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 168 | // attaching data adapter to spinner 169 | featureSpinner.setAdapter(dataAdapter); 170 | featureSpinner.setOnItemSelectedListener( 171 | new OnItemSelectedListener() { 172 | 173 | @Override 174 | public void onItemSelected( 175 | AdapterView parentView, View selectedItemView, int pos, long id) { 176 | selectedMode = parentView.getItemAtPosition(pos).toString(); 177 | createImageProcessor(); 178 | tryReloadAndDetectInImage(); 179 | } 180 | 181 | @Override 182 | public void onNothingSelected(AdapterView arg0) {} 183 | }); 184 | } 185 | 186 | private void populateSizeSelector() { 187 | Spinner sizeSpinner = (Spinner) findViewById(R.id.sizeSelector); 188 | List options = new ArrayList<>(); 189 | options.add(SIZE_PREVIEW); 190 | options.add(SIZE_1024_768); 191 | options.add(SIZE_640_480); 192 | 193 | // Creating adapter for featureSpinner 194 | ArrayAdapter dataAdapter = new ArrayAdapter<>(this, R.layout.spinner_style, options); 195 | // Drop down layout style - list view with radio button 196 | dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 197 | // attaching data adapter to spinner 198 | sizeSpinner.setAdapter(dataAdapter); 199 | sizeSpinner.setOnItemSelectedListener( 200 | new OnItemSelectedListener() { 201 | 202 | @Override 203 | public void onItemSelected( 204 | AdapterView parentView, View selectedItemView, int pos, long id) { 205 | selectedSize = parentView.getItemAtPosition(pos).toString(); 206 | tryReloadAndDetectInImage(); 207 | } 208 | 209 | @Override 210 | public void onNothingSelected(AdapterView arg0) {} 211 | }); 212 | } 213 | 214 | @Override 215 | public void onSaveInstanceState(Bundle outState) { 216 | super.onSaveInstanceState(outState); 217 | 218 | outState.putParcelable(KEY_IMAGE_URI, imageUri); 219 | if (imageMaxWidth != null) { 220 | outState.putInt(KEY_IMAGE_MAX_WIDTH, imageMaxWidth); 221 | } 222 | if (imageMaxHeight != null) { 223 | outState.putInt(KEY_IMAGE_MAX_HEIGHT, imageMaxHeight); 224 | } 225 | outState.putString(KEY_SELECTED_SIZE, selectedSize); 226 | } 227 | 228 | private void startCameraIntentForResult() { 229 | // Clean up last time's image 230 | imageUri = null; 231 | preview.setImageBitmap(null); 232 | 233 | Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 234 | if (takePictureIntent.resolveActivity(getPackageManager()) != null) { 235 | ContentValues values = new ContentValues(); 236 | values.put(MediaStore.Images.Media.TITLE, "New Picture"); 237 | values.put(MediaStore.Images.Media.DESCRIPTION, "From Camera"); 238 | imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); 239 | takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); 240 | startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE); 241 | } 242 | } 243 | 244 | private void startChooseImageIntentForResult() { 245 | Intent intent = new Intent(); 246 | intent.setType("image/*"); 247 | intent.setAction(Intent.ACTION_GET_CONTENT); 248 | startActivityForResult(Intent.createChooser(intent, "Select Picture"), REQUEST_CHOOSE_IMAGE); 249 | } 250 | 251 | @Override 252 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 253 | if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) { 254 | tryReloadAndDetectInImage(); 255 | } else if (requestCode == REQUEST_CHOOSE_IMAGE && resultCode == RESULT_OK) { 256 | // In this case, imageUri is returned by the chooser, save it. 257 | imageUri = data.getData(); 258 | tryReloadAndDetectInImage(); 259 | } 260 | } 261 | 262 | private void tryReloadAndDetectInImage() { 263 | try { 264 | if (imageUri == null) { 265 | return; 266 | } 267 | 268 | // Clear the overlay first 269 | graphicOverlay.clear(); 270 | 271 | Bitmap imageBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri); 272 | 273 | // Get the dimensions of the View 274 | Pair targetedSize = getTargetedWidthHeight(); 275 | 276 | int targetWidth = targetedSize.first; 277 | int maxHeight = targetedSize.second; 278 | 279 | // Determine how much to scale down the image 280 | float scaleFactor = 281 | Math.max( 282 | (float) imageBitmap.getWidth() / (float) targetWidth, 283 | (float) imageBitmap.getHeight() / (float) maxHeight); 284 | 285 | Bitmap resizedBitmap = 286 | Bitmap.createScaledBitmap( 287 | imageBitmap, 288 | (int) (imageBitmap.getWidth() / scaleFactor), 289 | (int) (imageBitmap.getHeight() / scaleFactor), 290 | true); 291 | 292 | preview.setImageBitmap(resizedBitmap); 293 | bitmapForDetection = resizedBitmap; 294 | 295 | imageProcessor.process(bitmapForDetection, graphicOverlay); 296 | } catch (IOException e) { 297 | Log.e(TAG, "Error retrieving saved image"); 298 | } 299 | } 300 | 301 | // Returns max image width, always for portrait mode. Caller needs to swap width / height for 302 | // landscape mode. 303 | private Integer getImageMaxWidth() { 304 | if (imageMaxWidth == null) { 305 | // Calculate the max width in portrait mode. This is done lazily since we need to wait for 306 | // a UI layout pass to get the right values. So delay it to first time image rendering time. 307 | if (isLandScape) { 308 | imageMaxWidth = 309 | ((View) preview.getParent()).getHeight() - findViewById(R.id.controlPanel).getHeight(); 310 | } else { 311 | imageMaxWidth = ((View) preview.getParent()).getWidth(); 312 | } 313 | } 314 | 315 | return imageMaxWidth; 316 | } 317 | 318 | // Returns max image height, always for portrait mode. Caller needs to swap width / height for 319 | // landscape mode. 320 | private Integer getImageMaxHeight() { 321 | if (imageMaxHeight == null) { 322 | // Calculate the max width in portrait mode. This is done lazily since we need to wait for 323 | // a UI layout pass to get the right values. So delay it to first time image rendering time. 324 | if (isLandScape) { 325 | imageMaxHeight = ((View) preview.getParent()).getWidth(); 326 | } else { 327 | imageMaxHeight = 328 | ((View) preview.getParent()).getHeight() - findViewById(R.id.controlPanel).getHeight(); 329 | } 330 | } 331 | 332 | return imageMaxHeight; 333 | } 334 | 335 | // Gets the targeted width / height. 336 | private Pair getTargetedWidthHeight() { 337 | int targetWidth; 338 | int targetHeight; 339 | 340 | switch (selectedSize) { 341 | case SIZE_PREVIEW: 342 | int maxWidthForPortraitMode = getImageMaxWidth(); 343 | int maxHeightForPortraitMode = getImageMaxHeight(); 344 | targetWidth = isLandScape ? maxHeightForPortraitMode : maxWidthForPortraitMode; 345 | targetHeight = isLandScape ? maxWidthForPortraitMode : maxHeightForPortraitMode; 346 | break; 347 | case SIZE_640_480: 348 | targetWidth = isLandScape ? 640 : 480; 349 | targetHeight = isLandScape ? 480 : 640; 350 | break; 351 | case SIZE_1024_768: 352 | targetWidth = isLandScape ? 1024 : 768; 353 | targetHeight = isLandScape ? 768 : 1024; 354 | break; 355 | default: 356 | throw new IllegalStateException("Unknown size"); 357 | } 358 | 359 | return new Pair<>(targetWidth, targetHeight); 360 | } 361 | 362 | private void createImageProcessor() { 363 | switch (selectedMode) { 364 | case CLOUD_LABEL_DETECTION: 365 | imageProcessor = new CloudImageLabelingProcessor(); 366 | break; 367 | case CLOUD_LANDMARK_DETECTION: 368 | imageProcessor = new CloudLandmarkRecognitionProcessor(); 369 | break; 370 | case CLOUD_TEXT_DETECTION: 371 | imageProcessor = new CloudTextRecognitionProcessor(); 372 | break; 373 | case CLOUD_DOCUMENT_TEXT_DETECTION: 374 | imageProcessor = new CloudDocumentTextRecognitionProcessor(); 375 | break; 376 | default: 377 | throw new IllegalStateException("Unknown selectedMode: " + selectedMode); 378 | } 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/VisionImageProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.graphics.Bitmap; 17 | import android.media.Image; 18 | 19 | import com.google.firebase.ml.common.FirebaseMLException; 20 | 21 | import java.nio.ByteBuffer; 22 | 23 | /** An inferface to process the images with different ML Kit detectors and custom image models. */ 24 | public interface VisionImageProcessor { 25 | 26 | /** Processes the images with the underlying machine learning models. */ 27 | void process(ByteBuffer data, FrameMetadata frameMetadata, GraphicOverlay graphicOverlay) 28 | throws FirebaseMLException; 29 | 30 | /** Processes the bitmap images. */ 31 | void process(Bitmap bitmap, GraphicOverlay graphicOverlay); 32 | 33 | /** Processes the images. */ 34 | void process(Image bitmap, int rotation, GraphicOverlay graphicOverlay); 35 | 36 | /** Stops the underlying machine learning model and release resources. */ 37 | void stop(); 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/VisionProcessorBase.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit; 15 | 16 | import android.graphics.Bitmap; 17 | import android.media.Image; 18 | import android.support.annotation.NonNull; 19 | 20 | import com.google.android.gms.tasks.OnFailureListener; 21 | import com.google.android.gms.tasks.OnSuccessListener; 22 | import com.google.android.gms.tasks.Task; 23 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 24 | import com.google.firebase.ml.vision.common.FirebaseVisionImageMetadata; 25 | 26 | import java.nio.ByteBuffer; 27 | import java.util.concurrent.atomic.AtomicBoolean; 28 | 29 | /** 30 | * Abstract base class for ML Kit frame processors. Subclasses need to implement {@link 31 | * #onSuccess(T, FrameMetadata, GraphicOverlay)} to define what they want to with the detection 32 | * results and {@link #detectInImage(FirebaseVisionImage)} to specify the detector object. 33 | * 34 | * @param The type of the detected feature. 35 | */ 36 | public abstract class VisionProcessorBase implements VisionImageProcessor { 37 | 38 | // Whether we should ignore process(). This is usually caused by feeding input data faster than 39 | // the model can handle. 40 | private final AtomicBoolean shouldThrottle = new AtomicBoolean(false); 41 | 42 | public VisionProcessorBase() { 43 | } 44 | 45 | @Override 46 | public void process( 47 | ByteBuffer data, final FrameMetadata frameMetadata, final GraphicOverlay 48 | graphicOverlay) { 49 | if (shouldThrottle.get()) { 50 | return; 51 | } 52 | FirebaseVisionImageMetadata metadata = 53 | new FirebaseVisionImageMetadata.Builder() 54 | .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) 55 | .setWidth(frameMetadata.getWidth()) 56 | .setHeight(frameMetadata.getHeight()) 57 | .setRotation(frameMetadata.getRotation()) 58 | .build(); 59 | 60 | detectInVisionImage( 61 | FirebaseVisionImage.fromByteBuffer(data, metadata), frameMetadata, graphicOverlay); 62 | } 63 | 64 | // Bitmap version 65 | @Override 66 | public void process(Bitmap bitmap, final GraphicOverlay 67 | graphicOverlay) { 68 | if (shouldThrottle.get()) { 69 | return; 70 | } 71 | detectInVisionImage(FirebaseVisionImage.fromBitmap(bitmap), null, graphicOverlay); 72 | } 73 | 74 | /** 75 | * Detects feature from given media.Image 76 | * 77 | * @return created FirebaseVisionImage 78 | */ 79 | @Override 80 | public void process(Image image, int rotation, final GraphicOverlay graphicOverlay) { 81 | if (shouldThrottle.get()) { 82 | return; 83 | } 84 | // This is for overlay display's usage 85 | FrameMetadata frameMetadata = 86 | new FrameMetadata.Builder().setWidth(image.getWidth()).setHeight(image.getHeight 87 | ()).build(); 88 | FirebaseVisionImage fbVisionImage = 89 | FirebaseVisionImage.fromMediaImage(image, rotation); 90 | detectInVisionImage(fbVisionImage, frameMetadata, graphicOverlay); 91 | } 92 | 93 | private void detectInVisionImage( 94 | FirebaseVisionImage image, 95 | final FrameMetadata metadata, 96 | final GraphicOverlay graphicOverlay) { 97 | detectInImage(image) 98 | .addOnSuccessListener( 99 | new OnSuccessListener() { 100 | @Override 101 | public void onSuccess(T results) { 102 | shouldThrottle.set(false); 103 | VisionProcessorBase.this.onSuccess(results, metadata, 104 | graphicOverlay); 105 | } 106 | }) 107 | .addOnFailureListener( 108 | new OnFailureListener() { 109 | @Override 110 | public void onFailure(@NonNull Exception e) { 111 | shouldThrottle.set(false); 112 | VisionProcessorBase.this.onFailure(e); 113 | } 114 | }); 115 | // Begin throttling until this frame of input has been processed, either in onSuccess or 116 | // onFailure. 117 | shouldThrottle.set(true); 118 | } 119 | 120 | @Override 121 | public void stop() { 122 | } 123 | 124 | protected abstract Task detectInImage(FirebaseVisionImage image); 125 | 126 | protected abstract void onSuccess( 127 | @NonNull T results, 128 | @NonNull FrameMetadata frameMetadata, 129 | @NonNull GraphicOverlay graphicOverlay); 130 | 131 | protected abstract void onFailure(@NonNull Exception e); 132 | } 133 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/barcodescanning/BarcodeGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.barcodescanning; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | import android.graphics.RectF; 20 | 21 | import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode; 22 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 23 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 24 | 25 | /** Graphic instance for rendering Barcode position and content information in an overlay view. */ 26 | public class BarcodeGraphic extends Graphic { 27 | 28 | private static final int TEXT_COLOR = Color.WHITE; 29 | private static final float TEXT_SIZE = 54.0f; 30 | private static final float STROKE_WIDTH = 4.0f; 31 | 32 | private final Paint rectPaint; 33 | private final Paint barcodePaint; 34 | private final FirebaseVisionBarcode barcode; 35 | 36 | BarcodeGraphic(GraphicOverlay overlay, FirebaseVisionBarcode barcode) { 37 | super(overlay); 38 | 39 | this.barcode = barcode; 40 | 41 | rectPaint = new Paint(); 42 | rectPaint.setColor(TEXT_COLOR); 43 | rectPaint.setStyle(Paint.Style.STROKE); 44 | rectPaint.setStrokeWidth(STROKE_WIDTH); 45 | 46 | barcodePaint = new Paint(); 47 | barcodePaint.setColor(TEXT_COLOR); 48 | barcodePaint.setTextSize(TEXT_SIZE); 49 | // Redraw the overlay, as this graphic has been added. 50 | postInvalidate(); 51 | } 52 | 53 | /** 54 | * Draws the barcode block annotations for position, size, and raw value on the supplied canvas. 55 | */ 56 | @Override 57 | public void draw(Canvas canvas) { 58 | if (barcode == null) { 59 | throw new IllegalStateException("Attempting to draw a null barcode."); 60 | } 61 | 62 | // Draws the bounding box around the BarcodeBlock. 63 | RectF rect = new RectF(barcode.getBoundingBox()); 64 | rect.left = translateX(rect.left); 65 | rect.top = translateY(rect.top); 66 | rect.right = translateX(rect.right); 67 | rect.bottom = translateY(rect.bottom); 68 | canvas.drawRect(rect, rectPaint); 69 | 70 | // Renders the barcode at the bottom of the box. 71 | canvas.drawText(barcode.getRawValue(), rect.left, rect.bottom, barcodePaint); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/barcodescanning/BarcodeScanningProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.barcodescanning; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcode; 22 | import com.google.firebase.ml.vision.barcode.FirebaseVisionBarcodeDetector; 23 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 24 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 25 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 26 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 27 | 28 | import java.io.IOException; 29 | import java.util.List; 30 | 31 | /** Barcode Detector Demo. */ 32 | public class BarcodeScanningProcessor extends VisionProcessorBase> { 33 | 34 | private static final String TAG = "BarcodeScanningProcessor"; 35 | 36 | private final FirebaseVisionBarcodeDetector detector; 37 | 38 | public BarcodeScanningProcessor() { 39 | // Note that if you know which format of barcode your app is dealing with, detection will be 40 | // faster to specify the supported barcode formats one by one, e.g. 41 | // new FirebaseVisionBarcodeDetectorOptions.Builder() 42 | // .setBarcodeFormats(irebaseVisionBarcode.FORMAT_QR_CODE) 43 | // .build(); 44 | detector = FirebaseVision.getInstance().getVisionBarcodeDetector(); 45 | } 46 | 47 | @Override 48 | public void stop() { 49 | try { 50 | detector.close(); 51 | } catch (IOException e) { 52 | Log.e(TAG, "Exception thrown while trying to close Barcode Detector: " + e); 53 | } 54 | } 55 | 56 | @Override 57 | protected Task> detectInImage(FirebaseVisionImage image) { 58 | return detector.detectInImage(image); 59 | } 60 | 61 | @Override 62 | protected void onSuccess( 63 | @NonNull List barcodes, 64 | @NonNull FrameMetadata frameMetadata, 65 | @NonNull GraphicOverlay graphicOverlay) { 66 | graphicOverlay.clear(); 67 | for (int i = 0; i < barcodes.size(); ++i) { 68 | FirebaseVisionBarcode barcode = barcodes.get(i); 69 | BarcodeGraphic barcodeGraphic = new BarcodeGraphic(graphicOverlay, barcode); 70 | graphicOverlay.add(barcodeGraphic); 71 | } 72 | } 73 | 74 | @Override 75 | protected void onFailure(@NonNull Exception e) { 76 | Log.e(TAG, "Barcode detection failed " + e); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudimagelabeling/CloudImageLabelingProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudimagelabeling; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.cloud.FirebaseVisionCloudDetectorOptions; 22 | import com.google.firebase.ml.vision.cloud.label.FirebaseVisionCloudLabel; 23 | import com.google.firebase.ml.vision.cloud.label.FirebaseVisionCloudLabelDetector; 24 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 25 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 26 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 27 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 28 | 29 | import java.util.ArrayList; 30 | import java.util.List; 31 | 32 | /** Cloud Label Detector Demo. */ 33 | public class CloudImageLabelingProcessor 34 | extends VisionProcessorBase> { 35 | private static final String TAG = "CloudImageLabelingProcessor"; 36 | 37 | private final FirebaseVisionCloudLabelDetector detector; 38 | 39 | public CloudImageLabelingProcessor() { 40 | FirebaseVisionCloudDetectorOptions options = 41 | new FirebaseVisionCloudDetectorOptions.Builder() 42 | .setMaxResults(10) 43 | .setModelType(FirebaseVisionCloudDetectorOptions.STABLE_MODEL) 44 | .build(); 45 | 46 | detector = FirebaseVision.getInstance().getVisionCloudLabelDetector(options); 47 | } 48 | 49 | @Override 50 | protected Task> detectInImage(FirebaseVisionImage image) { 51 | return detector.detectInImage(image); 52 | } 53 | 54 | @Override 55 | protected void onSuccess( 56 | @NonNull List labels, 57 | @NonNull FrameMetadata frameMetadata, 58 | @NonNull GraphicOverlay graphicOverlay) { 59 | graphicOverlay.clear(); 60 | Log.d(TAG, "cloud label size: " + labels.size()); 61 | List labelsStr = new ArrayList<>(); 62 | for (int i = 0; i < labels.size(); ++i) { 63 | FirebaseVisionCloudLabel label = labels.get(i); 64 | Log.d(TAG, "cloud label: " + label); 65 | if (label.getLabel() != null) { 66 | labelsStr.add((label.getLabel())); 67 | } 68 | } 69 | CloudLabelGraphic cloudLabelGraphic = new CloudLabelGraphic(graphicOverlay); 70 | graphicOverlay.add(cloudLabelGraphic); 71 | cloudLabelGraphic.updateLabel(labelsStr); 72 | } 73 | 74 | @Override 75 | protected void onFailure(@NonNull Exception e) { 76 | Log.e(TAG, "Cloud Label detection failed " + e); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudimagelabeling/CloudLabelGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudimagelabeling; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | 20 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 21 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 22 | 23 | import java.util.List; 24 | 25 | /** Graphic instance for rendering detected label. */ 26 | public class CloudLabelGraphic extends Graphic { 27 | private final Paint textPaint; 28 | private final GraphicOverlay overlay; 29 | 30 | private List labels; 31 | 32 | CloudLabelGraphic(GraphicOverlay overlay) { 33 | super(overlay); 34 | this.overlay = overlay; 35 | textPaint = new Paint(); 36 | textPaint.setColor(Color.WHITE); 37 | textPaint.setTextSize(60.0f); 38 | } 39 | 40 | synchronized void updateLabel(List labels) { 41 | this.labels = labels; 42 | postInvalidate(); 43 | } 44 | 45 | @Override 46 | public synchronized void draw(Canvas canvas) { 47 | float x = overlay.getWidth() / 4.0f; 48 | float y = overlay.getHeight() / 4.0f; 49 | 50 | for (String label : labels) { 51 | canvas.drawText(label, x, y, textPaint); 52 | y = y - 62.0f; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudlandmarkrecognition/CloudLandmarkGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudlandmarkrecognition; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | import android.graphics.RectF; 20 | 21 | import com.google.firebase.ml.vision.cloud.landmark.FirebaseVisionCloudLandmark; 22 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 23 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 24 | 25 | /** Graphic instance for rendering detected landmark. */ 26 | public class CloudLandmarkGraphic extends Graphic { 27 | private static final int TEXT_COLOR = Color.WHITE; 28 | private static final float TEXT_SIZE = 54.0f; 29 | private static final float STROKE_WIDTH = 4.0f; 30 | 31 | private final Paint rectPaint; 32 | private final Paint landmarkPaint; 33 | private FirebaseVisionCloudLandmark landmark; 34 | 35 | CloudLandmarkGraphic(GraphicOverlay overlay) { 36 | super(overlay); 37 | 38 | rectPaint = new Paint(); 39 | rectPaint.setColor(TEXT_COLOR); 40 | rectPaint.setStyle(Paint.Style.STROKE); 41 | rectPaint.setStrokeWidth(STROKE_WIDTH); 42 | 43 | landmarkPaint = new Paint(); 44 | landmarkPaint.setColor(TEXT_COLOR); 45 | landmarkPaint.setTextSize(TEXT_SIZE); 46 | } 47 | 48 | /** 49 | * Updates the landmark instance from the detection of the most recent frame. Invalidates the 50 | * relevant portions of the overlay to trigger a redraw. 51 | */ 52 | void updateLandmark(FirebaseVisionCloudLandmark landmark) { 53 | this.landmark = landmark; 54 | postInvalidate(); 55 | } 56 | 57 | /** 58 | * Draws the landmark block annotations for position, size, and raw value on the supplied canvas. 59 | */ 60 | @Override 61 | public void draw(Canvas canvas) { 62 | if (landmark == null) { 63 | throw new IllegalStateException("Attempting to draw a null landmark."); 64 | } 65 | if (landmark.getLandmark() == null || landmark.getBoundingBox() == null) { 66 | return; 67 | } 68 | 69 | // Draws the bounding box around the LandmarkBlock. 70 | RectF rect = new RectF(landmark.getBoundingBox()); 71 | rect.left = translateX(rect.left); 72 | rect.top = translateY(rect.top); 73 | rect.right = translateX(rect.right); 74 | rect.bottom = translateY(rect.bottom); 75 | canvas.drawRect(rect, rectPaint); 76 | 77 | // Renders the landmark at the bottom of the box. 78 | canvas.drawText(landmark.getLandmark(), rect.left, rect.bottom, landmarkPaint); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudlandmarkrecognition/CloudLandmarkRecognitionProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudlandmarkrecognition; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.cloud.FirebaseVisionCloudDetectorOptions; 22 | import com.google.firebase.ml.vision.cloud.landmark.FirebaseVisionCloudLandmark; 23 | import com.google.firebase.ml.vision.cloud.landmark.FirebaseVisionCloudLandmarkDetector; 24 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 25 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 26 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 27 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 28 | 29 | import java.util.List; 30 | 31 | /** Cloud Landmark Detector Demo. */ 32 | public class CloudLandmarkRecognitionProcessor 33 | extends VisionProcessorBase> { 34 | private static final String TAG = "CloudLandmarkRecognitionProcessor"; 35 | 36 | private final FirebaseVisionCloudLandmarkDetector detector; 37 | 38 | public CloudLandmarkRecognitionProcessor() { 39 | super(); 40 | FirebaseVisionCloudDetectorOptions options = 41 | new FirebaseVisionCloudDetectorOptions.Builder() 42 | .setMaxResults(10) 43 | .setModelType(FirebaseVisionCloudDetectorOptions.STABLE_MODEL) 44 | .build(); 45 | 46 | detector = FirebaseVision.getInstance().getVisionCloudLandmarkDetector(options); 47 | } 48 | 49 | @Override 50 | protected Task> detectInImage(FirebaseVisionImage image) { 51 | return detector.detectInImage(image); 52 | } 53 | 54 | @Override 55 | protected void onSuccess( 56 | @NonNull List landmarks, 57 | @NonNull FrameMetadata frameMetadata, 58 | @NonNull GraphicOverlay graphicOverlay) { 59 | graphicOverlay.clear(); 60 | Log.d(TAG, "cloud landmark size: " + landmarks.size()); 61 | for (int i = 0; i < landmarks.size(); ++i) { 62 | FirebaseVisionCloudLandmark landmark = landmarks.get(i); 63 | Log.d(TAG, "cloud landmark: " + landmark); 64 | CloudLandmarkGraphic cloudLandmarkGraphic = new CloudLandmarkGraphic(graphicOverlay); 65 | graphicOverlay.add(cloudLandmarkGraphic); 66 | cloudLandmarkGraphic.updateLandmark(landmark); 67 | } 68 | } 69 | 70 | @Override 71 | protected void onFailure(@NonNull Exception e) { 72 | Log.e(TAG, "Cloud Landmark detection failed " + e); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudtextrecognition/CloudDocumentTextRecognitionProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudtextrecognition; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.cloud.text.FirebaseVisionCloudDocumentTextDetector; 22 | import com.google.firebase.ml.vision.cloud.text.FirebaseVisionCloudText; 23 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 24 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 25 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 26 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 27 | 28 | /** Processor for the cloud document text detector demo. */ 29 | public class CloudDocumentTextRecognitionProcessor 30 | extends VisionProcessorBase { 31 | 32 | private static final String TAG = "CloudDocumentTextRecognitionProcessor"; 33 | 34 | private final FirebaseVisionCloudDocumentTextDetector detector; 35 | 36 | public CloudDocumentTextRecognitionProcessor() { 37 | super(); 38 | detector = FirebaseVision.getInstance().getVisionCloudDocumentTextDetector(); 39 | } 40 | 41 | @Override 42 | protected Task detectInImage(FirebaseVisionImage image) { 43 | return detector.detectInImage(image); 44 | } 45 | 46 | @Override 47 | protected void onSuccess( 48 | @NonNull FirebaseVisionCloudText text, 49 | @NonNull FrameMetadata frameMetadata, 50 | @NonNull GraphicOverlay graphicOverlay) { 51 | graphicOverlay.clear(); 52 | Log.d(TAG, "detected text is: " + text.getText()); 53 | CloudTextGraphic textGraphic = new CloudTextGraphic(graphicOverlay, text); 54 | graphicOverlay.add(textGraphic); 55 | } 56 | 57 | @Override 58 | protected void onFailure(@NonNull Exception e) { 59 | Log.w(TAG, "Cloud Document Text detection failed." + e); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudtextrecognition/CloudTextGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudtextrecognition; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | 20 | import com.google.firebase.ml.vision.cloud.text.FirebaseVisionCloudText; 21 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 22 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 23 | 24 | /** 25 | * Graphic instance for rendering TextBlock position, size, and ID within an associated graphic 26 | * overlay view. 27 | */ 28 | public class CloudTextGraphic extends Graphic { 29 | private static final int TEXT_COLOR = Color.WHITE; 30 | private static final float TEXT_SIZE = 54.0f; 31 | private static final float STROKE_WIDTH = 4.0f; 32 | 33 | private final Paint rectPaint; 34 | private final Paint textPaint; 35 | private final FirebaseVisionCloudText text; 36 | private final GraphicOverlay overlay; 37 | 38 | CloudTextGraphic(GraphicOverlay overlay, FirebaseVisionCloudText text) { 39 | super(overlay); 40 | 41 | this.text = text; 42 | this.overlay = overlay; 43 | 44 | rectPaint = new Paint(); 45 | rectPaint.setColor(TEXT_COLOR); 46 | rectPaint.setStyle(Paint.Style.STROKE); 47 | rectPaint.setStrokeWidth(STROKE_WIDTH); 48 | 49 | textPaint = new Paint(); 50 | textPaint.setColor(TEXT_COLOR); 51 | textPaint.setTextSize(TEXT_SIZE); 52 | // Redraw the overlay, as this graphic has been added. 53 | postInvalidate(); 54 | } 55 | 56 | /** Draws the text block annotations for position, size, and raw value on the supplied canvas. */ 57 | @Override 58 | public void draw(Canvas canvas) { 59 | if (text == null) { 60 | throw new IllegalStateException("Attempting to draw a null text."); 61 | } 62 | 63 | float x = overlay.getWidth() / 4.0f; 64 | float y = overlay.getHeight() / 4.0f; 65 | 66 | canvas.drawText(text.getText(), x, y, textPaint); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/cloudtextrecognition/CloudTextRecognitionProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.cloudtextrecognition; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.cloud.text.FirebaseVisionCloudText; 22 | import com.google.firebase.ml.vision.cloud.text.FirebaseVisionCloudTextDetector; 23 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 24 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 25 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 26 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 27 | 28 | /** Processor for the cloud text detector demo. */ 29 | public class CloudTextRecognitionProcessor extends VisionProcessorBase { 30 | 31 | private static final String TAG = "CloudTextRecognitionProcessor"; 32 | 33 | private final FirebaseVisionCloudTextDetector detector; 34 | 35 | public CloudTextRecognitionProcessor() { 36 | super(); 37 | detector = FirebaseVision.getInstance().getVisionCloudTextDetector(); 38 | } 39 | 40 | @Override 41 | protected Task detectInImage(FirebaseVisionImage image) { 42 | return detector.detectInImage(image); 43 | } 44 | 45 | @Override 46 | protected void onSuccess( 47 | @NonNull FirebaseVisionCloudText text, 48 | @NonNull FrameMetadata frameMetadata, 49 | @NonNull GraphicOverlay graphicOverlay) { 50 | graphicOverlay.clear(); 51 | Log.d(TAG, "detected text is: " + text.getText()); 52 | CloudTextGraphic textGraphic = new CloudTextGraphic(graphicOverlay, text); 53 | graphicOverlay.add(textGraphic); 54 | } 55 | 56 | @Override 57 | protected void onFailure(@NonNull Exception e) { 58 | Log.w(TAG, "Cloud Text detection failed." + e); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/custommodel/CustomImageClassifier.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.custommodel; 15 | 16 | import android.app.Activity; 17 | import android.graphics.Bitmap; 18 | import android.graphics.BitmapFactory; 19 | import android.graphics.ImageFormat; 20 | import android.graphics.Rect; 21 | import android.graphics.YuvImage; 22 | import android.os.SystemClock; 23 | import android.util.Log; 24 | 25 | import com.google.android.gms.tasks.Continuation; 26 | import com.google.android.gms.tasks.Task; 27 | import com.google.android.gms.tasks.Tasks; 28 | import com.google.firebase.ml.common.FirebaseMLException; 29 | import com.google.firebase.ml.custom.FirebaseModelDataType; 30 | import com.google.firebase.ml.custom.FirebaseModelInputOutputOptions; 31 | import com.google.firebase.ml.custom.FirebaseModelInputs; 32 | import com.google.firebase.ml.custom.FirebaseModelInterpreter; 33 | import com.google.firebase.ml.custom.FirebaseModelOptions; 34 | import com.google.firebase.ml.custom.FirebaseModelOutputs; 35 | 36 | import java.io.BufferedReader; 37 | import java.io.ByteArrayOutputStream; 38 | import java.io.IOException; 39 | import java.io.InputStreamReader; 40 | import java.nio.ByteBuffer; 41 | import java.nio.ByteOrder; 42 | import java.util.AbstractMap; 43 | import java.util.ArrayList; 44 | import java.util.Comparator; 45 | import java.util.List; 46 | import java.util.Map; 47 | import java.util.PriorityQueue; 48 | 49 | /** A {@code FirebaseModelInterpreter} based image classifier. */ 50 | public class CustomImageClassifier { 51 | 52 | /** Tag for the {@link Log}. */ 53 | private static final String TAG = "MLKitDemoApp:Classifier"; 54 | 55 | /** Name of the model file stored in Assets. */ 56 | private static final String MODEL_PATH = "mobilenet_quant_v1_224.tflite"; 57 | 58 | /** Name of the label file stored in Assets. */ 59 | private static final String LABEL_PATH = "labels.txt"; 60 | 61 | /** Number of results to show in the UI. */ 62 | private static final int RESULTS_TO_SHOW = 3; 63 | 64 | /** Dimensions of inputs. */ 65 | private static final int DIM_BATCH_SIZE = 1; 66 | 67 | private static final int DIM_PIXEL_SIZE = 3; 68 | 69 | private static final int DIM_IMG_SIZE_X = 224; 70 | private static final int DIM_IMG_SIZE_Y = 224; 71 | 72 | /* Preallocated buffers for storing image data in. */ 73 | private final int[] intValues = new int[DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y]; 74 | 75 | /** An instance of the driver class to run model inference with Firebase. */ 76 | private final FirebaseModelInterpreter interpreter; 77 | 78 | /** Data configuration of input & output data of model. */ 79 | private final FirebaseModelInputOutputOptions dataOptions; 80 | 81 | /** Labels corresponding to the output of the vision model. */ 82 | private final List labelList; 83 | 84 | private final PriorityQueue> sortedLabels = 85 | new PriorityQueue<>( 86 | RESULTS_TO_SHOW, 87 | new Comparator>() { 88 | @Override 89 | public int compare(Map.Entry o1, Map.Entry o2) { 90 | return (o1.getValue()).compareTo(o2.getValue()); 91 | } 92 | }); 93 | 94 | /** Initializes an {@code CustomImageClassifier}. */ 95 | CustomImageClassifier(Activity activity) throws FirebaseMLException { 96 | FirebaseModelOptions modelOptions = 97 | new FirebaseModelOptions.Builder() 98 | .setCloudModelName("mobilenet_v1") 99 | .setLocalModelName(MODEL_PATH) 100 | .build(); 101 | interpreter = FirebaseModelInterpreter.getInstance(modelOptions); 102 | labelList = loadLabelList(activity); 103 | Log.d(TAG, "Created a Custom Image Classifier."); 104 | int[] inputDims = {DIM_BATCH_SIZE, DIM_IMG_SIZE_X, DIM_IMG_SIZE_Y, DIM_PIXEL_SIZE}; 105 | int[] outputDims = {1, labelList.size()}; 106 | dataOptions = 107 | new FirebaseModelInputOutputOptions.Builder() 108 | .setInputFormat(0, FirebaseModelDataType.BYTE, inputDims) 109 | .setOutputFormat(0, FirebaseModelDataType.BYTE, outputDims) 110 | .build(); 111 | Log.d(TAG, "Configured input & output data for the custom image classifier."); 112 | } 113 | 114 | /** Classifies a frame from the preview stream. */ 115 | Task> classifyFrame(ByteBuffer buffer, int width, int height) 116 | throws FirebaseMLException { 117 | if (interpreter == null) { 118 | Log.e(TAG, "Image classifier has not been initialized; Skipped."); 119 | List uninitialized = new ArrayList<>(); 120 | uninitialized.add("Uninitialized Classifier."); 121 | Tasks.forResult(uninitialized); 122 | } 123 | // Create input data. 124 | ByteBuffer imgData = convertBitmapToByteBuffer(buffer, width, height); 125 | 126 | FirebaseModelInputs inputs = new FirebaseModelInputs.Builder().add(imgData).build(); 127 | // Here's where the magic happens!! 128 | return interpreter 129 | .run(inputs, dataOptions) 130 | .continueWith( 131 | new Continuation>() { 132 | @Override 133 | public List then(Task task) throws Exception { 134 | byte[][] labelProbArray = task.getResult().getOutput(0); 135 | return printTopKLabels(labelProbArray); 136 | } 137 | }); 138 | } 139 | 140 | /** Reads label list from Assets. */ 141 | private List loadLabelList(Activity activity) { 142 | List labelList = new ArrayList<>(); 143 | try (BufferedReader reader = 144 | new BufferedReader(new InputStreamReader(activity.getAssets().open(LABEL_PATH)))) { 145 | String line; 146 | while ((line = reader.readLine()) != null) { 147 | labelList.add(line); 148 | } 149 | } catch (IOException e) { 150 | Log.e(TAG, "Failed to read label list.", e); 151 | } 152 | return labelList; 153 | } 154 | 155 | /** Writes Image data into a {@code ByteBuffer}. */ 156 | private synchronized ByteBuffer convertBitmapToByteBuffer( 157 | ByteBuffer buffer, int width, int height) { 158 | ByteBuffer imgData = 159 | ByteBuffer.allocateDirect( 160 | DIM_BATCH_SIZE * DIM_IMG_SIZE_X * DIM_IMG_SIZE_Y * DIM_PIXEL_SIZE); 161 | imgData.order(ByteOrder.nativeOrder()); 162 | Bitmap bitmap = createResizedBitmap(buffer, width, height); 163 | imgData.rewind(); 164 | bitmap.getPixels(intValues, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight()); 165 | // Convert the image to int points. 166 | int pixel = 0; 167 | long startTime = SystemClock.uptimeMillis(); 168 | for (int i = 0; i < DIM_IMG_SIZE_X; ++i) { 169 | for (int j = 0; j < DIM_IMG_SIZE_Y; ++j) { 170 | final int val = intValues[pixel++]; 171 | imgData.put((byte) ((val >> 16) & 0xFF)); 172 | imgData.put((byte) ((val >> 8) & 0xFF)); 173 | imgData.put((byte) (val & 0xFF)); 174 | } 175 | } 176 | long endTime = SystemClock.uptimeMillis(); 177 | Log.d(TAG, "Timecost to put values into ByteBuffer: " + (endTime - startTime)); 178 | return imgData; 179 | } 180 | 181 | /** Resizes image data from {@code ByteBuffer}. */ 182 | private Bitmap createResizedBitmap(ByteBuffer buffer, int width, int height) { 183 | YuvImage img = new YuvImage(buffer.array(), ImageFormat.NV21, width, height, null); 184 | ByteArrayOutputStream out = new ByteArrayOutputStream(); 185 | img.compressToJpeg(new Rect(0, 0, img.getWidth(), img.getHeight()), 50, out); 186 | byte[] imageBytes = out.toByteArray(); 187 | Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); 188 | return Bitmap.createScaledBitmap(bitmap, DIM_IMG_SIZE_X, DIM_IMG_SIZE_Y, true); 189 | } 190 | 191 | /** Prints top-K labels, to be shown in UI as the results. */ 192 | private synchronized List printTopKLabels(byte[][] labelProbArray) { 193 | for (int i = 0; i < labelList.size(); ++i) { 194 | sortedLabels.add( 195 | new AbstractMap.SimpleEntry<>(labelList.get(i), (labelProbArray[0][i] & 0xff) / 255.0f)); 196 | if (sortedLabels.size() > RESULTS_TO_SHOW) { 197 | sortedLabels.poll(); 198 | } 199 | } 200 | List result = new ArrayList<>(); 201 | final int size = sortedLabels.size(); 202 | for (int i = 0; i < size; ++i) { 203 | Map.Entry label = sortedLabels.poll(); 204 | result.add(label.getKey() + ":" + label.getValue()); 205 | } 206 | return result; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/custommodel/CustomImageClassifierProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.custommodel; 15 | 16 | import android.app.Activity; 17 | import android.graphics.Bitmap; 18 | import android.media.Image; 19 | 20 | import com.google.android.gms.tasks.OnSuccessListener; 21 | import com.google.firebase.ml.common.FirebaseMLException; 22 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 23 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 24 | import com.google.firebase.samples.apps.mlkit.VisionImageProcessor; 25 | 26 | import java.nio.ByteBuffer; 27 | import java.util.List; 28 | 29 | /** Custom Image Classifier Demo. */ 30 | public class CustomImageClassifierProcessor implements VisionImageProcessor { 31 | 32 | private final CustomImageClassifier classifier; 33 | private final Activity activity; 34 | 35 | public CustomImageClassifierProcessor(Activity activity) throws FirebaseMLException { 36 | this.activity = activity; 37 | classifier = new CustomImageClassifier(activity); 38 | } 39 | 40 | @Override 41 | public void process( 42 | ByteBuffer data, FrameMetadata frameMetadata, final GraphicOverlay graphicOverlay) 43 | throws FirebaseMLException { 44 | classifier 45 | .classifyFrame(data, frameMetadata.getWidth(), frameMetadata.getHeight()) 46 | .addOnSuccessListener( 47 | activity, 48 | new OnSuccessListener>() { 49 | @Override 50 | public void onSuccess(List result) { 51 | LabelGraphic labelGraphic = new LabelGraphic(graphicOverlay); 52 | graphicOverlay.clear(); 53 | graphicOverlay.add(labelGraphic); 54 | labelGraphic.updateLabel(result); 55 | } 56 | }); 57 | } 58 | 59 | @Override 60 | public void process(Bitmap bitmap, GraphicOverlay graphicOverlay) { 61 | // nop 62 | } 63 | 64 | @Override 65 | public void process(Image bitmap, int rotation, GraphicOverlay graphicOverlay) { 66 | // nop 67 | 68 | } 69 | 70 | @Override 71 | public void stop() {} 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/custommodel/LabelGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.custommodel; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | 20 | 21 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 22 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 23 | 24 | import java.util.List; 25 | 26 | /** Graphic instance for rendering image labels. */ 27 | public class LabelGraphic extends Graphic { 28 | 29 | private final Paint textPaint; 30 | private final GraphicOverlay overlay; 31 | 32 | private List labels; 33 | 34 | LabelGraphic(GraphicOverlay overlay) { 35 | super(overlay); 36 | this.overlay = overlay; 37 | textPaint = new Paint(); 38 | textPaint.setColor(Color.WHITE); 39 | textPaint.setTextSize(60.0f); 40 | } 41 | 42 | synchronized void updateLabel(List labels) { 43 | this.labels = labels; 44 | postInvalidate(); 45 | } 46 | 47 | @Override 48 | public synchronized void draw(Canvas canvas) { 49 | float x = overlay.getWidth() / 4.0f; 50 | float y = overlay.getHeight() / 4.0f; 51 | 52 | for (String label : labels) { 53 | canvas.drawText(label, x, y, textPaint); 54 | y = y - 62.0f; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/facedetection/FaceDetectionProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.facedetection; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 22 | import com.google.firebase.ml.vision.face.FirebaseVisionFace; 23 | import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetector; 24 | import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetectorOptions; 25 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 26 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 27 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 28 | 29 | import java.io.IOException; 30 | import java.util.List; 31 | 32 | /** Face Detector Demo. */ 33 | public class FaceDetectionProcessor extends VisionProcessorBase> { 34 | 35 | private static final String TAG = "FaceDetectionProcessor"; 36 | 37 | private final FirebaseVisionFaceDetector detector; 38 | 39 | public FaceDetectionProcessor() { 40 | FirebaseVisionFaceDetectorOptions options = 41 | new FirebaseVisionFaceDetectorOptions.Builder() 42 | .setClassificationType(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS) 43 | .setTrackingEnabled(true) 44 | .build(); 45 | 46 | detector = FirebaseVision.getInstance().getVisionFaceDetector(options); 47 | } 48 | 49 | @Override 50 | public void stop() { 51 | try { 52 | detector.close(); 53 | } catch (IOException e) { 54 | Log.e(TAG, "Exception thrown while trying to close Face Detector: " + e); 55 | } 56 | } 57 | 58 | @Override 59 | protected Task> detectInImage(FirebaseVisionImage image) { 60 | return detector.detectInImage(image); 61 | } 62 | 63 | @Override 64 | protected void onSuccess( 65 | @NonNull List faces, 66 | @NonNull FrameMetadata frameMetadata, 67 | @NonNull GraphicOverlay graphicOverlay) { 68 | graphicOverlay.clear(); 69 | for (int i = 0; i < faces.size(); ++i) { 70 | FirebaseVisionFace face = faces.get(i); 71 | FaceGraphic faceGraphic = new FaceGraphic(graphicOverlay); 72 | graphicOverlay.add(faceGraphic); 73 | faceGraphic.updateFace(face, frameMetadata.getCameraFacing()); 74 | } 75 | } 76 | 77 | @Override 78 | protected void onFailure(@NonNull Exception e) { 79 | Log.e(TAG, "Face detection failed " + e); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/facedetection/FaceGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package com.google.firebase.samples.apps.mlkit.facedetection; 16 | 17 | import android.graphics.Canvas; 18 | import android.graphics.Color; 19 | import android.graphics.Paint; 20 | 21 | import com.google.android.gms.vision.CameraSource; 22 | import com.google.firebase.ml.vision.face.FirebaseVisionFace; 23 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 24 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 25 | 26 | /** 27 | * Graphic instance for rendering face position, orientation, and landmarks within an associated 28 | * graphic overlay view. 29 | */ 30 | public class FaceGraphic extends Graphic { 31 | private static final float FACE_POSITION_RADIUS = 10.0f; 32 | private static final float ID_TEXT_SIZE = 40.0f; 33 | private static final float ID_Y_OFFSET = 50.0f; 34 | private static final float ID_X_OFFSET = -50.0f; 35 | private static final float BOX_STROKE_WIDTH = 5.0f; 36 | 37 | private static final int[] COLOR_CHOICES = { 38 | Color.BLUE //, Color.CYAN, Color.GREEN, Color.MAGENTA, Color.RED, Color.WHITE, Color.YELLOW 39 | }; 40 | private static int currentColorIndex = 0; 41 | 42 | private int facing; 43 | 44 | private final Paint facePositionPaint; 45 | private final Paint idPaint; 46 | private final Paint boxPaint; 47 | 48 | private volatile FirebaseVisionFace firebaseVisionFace; 49 | 50 | public FaceGraphic(GraphicOverlay overlay) { 51 | super(overlay); 52 | 53 | currentColorIndex = (currentColorIndex + 1) % COLOR_CHOICES.length; 54 | final int selectedColor = COLOR_CHOICES[currentColorIndex]; 55 | 56 | facePositionPaint = new Paint(); 57 | facePositionPaint.setColor(selectedColor); 58 | 59 | idPaint = new Paint(); 60 | idPaint.setColor(selectedColor); 61 | idPaint.setTextSize(ID_TEXT_SIZE); 62 | 63 | boxPaint = new Paint(); 64 | boxPaint.setColor(selectedColor); 65 | boxPaint.setStyle(Paint.Style.STROKE); 66 | boxPaint.setStrokeWidth(BOX_STROKE_WIDTH); 67 | } 68 | 69 | /** 70 | * Updates the face instance from the detection of the most recent frame. Invalidates the relevant 71 | * portions of the overlay to trigger a redraw. 72 | */ 73 | public void updateFace(FirebaseVisionFace face, int facing) { 74 | firebaseVisionFace = face; 75 | this.facing = facing; 76 | postInvalidate(); 77 | } 78 | 79 | /** Draws the face annotations for position on the supplied canvas. */ 80 | @Override 81 | public void draw(Canvas canvas) { 82 | FirebaseVisionFace face = firebaseVisionFace; 83 | if (face == null) { 84 | return; 85 | } 86 | 87 | // Draws a circle at the position of the detected face, with the face's track id below. 88 | float x = translateX(face.getBoundingBox().centerX()); 89 | float y = translateY(face.getBoundingBox().centerY()); 90 | canvas.drawCircle(x, y, FACE_POSITION_RADIUS, facePositionPaint); 91 | canvas.drawText("id: " + face.getTrackingId(), x + ID_X_OFFSET, y + ID_Y_OFFSET, idPaint); 92 | canvas.drawText( 93 | "happiness: " + String.format("%.2f", face.getSmilingProbability()), 94 | x + ID_X_OFFSET * 3, 95 | y - ID_Y_OFFSET, 96 | idPaint); 97 | if (facing == CameraSource.CAMERA_FACING_FRONT) { 98 | canvas.drawText( 99 | "right eye: " + String.format("%.2f", face.getRightEyeOpenProbability()), 100 | x - ID_X_OFFSET, 101 | y, 102 | idPaint); 103 | canvas.drawText( 104 | "left eye: " + String.format("%.2f", face.getLeftEyeOpenProbability()), 105 | x + ID_X_OFFSET * 6, 106 | y, 107 | idPaint); 108 | } else { 109 | canvas.drawText( 110 | "left eye: " + String.format("%.2f", face.getLeftEyeOpenProbability()), 111 | x - ID_X_OFFSET, 112 | y, 113 | idPaint); 114 | canvas.drawText( 115 | "right eye: " + String.format("%.2f", face.getRightEyeOpenProbability()), 116 | x + ID_X_OFFSET * 6, 117 | y, 118 | idPaint); 119 | } 120 | 121 | // Draws a bounding box around the face. 122 | float xOffset = scaleX(face.getBoundingBox().width() / 2.0f); 123 | float yOffset = scaleY(face.getBoundingBox().height() / 2.0f); 124 | float left = x - xOffset; 125 | float top = y - yOffset; 126 | float right = x + xOffset; 127 | float bottom = y + yOffset; 128 | canvas.drawRect(left, top, right, bottom, boxPaint); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/imagelabeling/ImageLabelingProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.imagelabeling; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 22 | import com.google.firebase.ml.vision.label.FirebaseVisionLabel; 23 | import com.google.firebase.ml.vision.label.FirebaseVisionLabelDetector; 24 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 25 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 26 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 27 | 28 | import java.io.IOException; 29 | import java.util.List; 30 | 31 | /** Custom Image Classifier Demo. */ 32 | public class ImageLabelingProcessor extends VisionProcessorBase> { 33 | 34 | private static final String TAG = "ImageLabelingProcessor"; 35 | 36 | private final FirebaseVisionLabelDetector detector; 37 | 38 | public ImageLabelingProcessor() { 39 | detector = FirebaseVision.getInstance().getVisionLabelDetector(); 40 | } 41 | 42 | @Override 43 | public void stop() { 44 | try { 45 | detector.close(); 46 | } catch (IOException e) { 47 | Log.e(TAG, "Exception thrown while trying to close Text Detector: " + e); 48 | } 49 | } 50 | 51 | @Override 52 | protected Task> detectInImage(FirebaseVisionImage image) { 53 | return detector.detectInImage(image); 54 | } 55 | 56 | @Override 57 | protected void onSuccess( 58 | @NonNull List labels, 59 | @NonNull FrameMetadata frameMetadata, 60 | @NonNull GraphicOverlay graphicOverlay) { 61 | graphicOverlay.clear(); 62 | LabelGraphic labelGraphic = new LabelGraphic(graphicOverlay, labels); 63 | graphicOverlay.add(labelGraphic); 64 | } 65 | 66 | @Override 67 | protected void onFailure(@NonNull Exception e) { 68 | Log.w(TAG, "Label detection failed." + e); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/imagelabeling/LabelGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.imagelabeling; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | 20 | import com.google.firebase.ml.vision.label.FirebaseVisionLabel; 21 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 22 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 23 | 24 | import java.util.List; 25 | 26 | /** Graphic instance for rendering a label within an associated graphic overlay view. */ 27 | public class LabelGraphic extends Graphic { 28 | 29 | private final Paint textPaint; 30 | private final GraphicOverlay overlay; 31 | 32 | private final List labels; 33 | 34 | LabelGraphic(GraphicOverlay overlay, List labels) { 35 | super(overlay); 36 | this.overlay = overlay; 37 | this.labels = labels; 38 | textPaint = new Paint(); 39 | textPaint.setColor(Color.WHITE); 40 | textPaint.setTextSize(60.0f); 41 | postInvalidate(); 42 | } 43 | 44 | @Override 45 | public synchronized void draw(Canvas canvas) { 46 | float x = overlay.getWidth() / 4.0f; 47 | float y = overlay.getHeight() / 2.0f; 48 | 49 | for (FirebaseVisionLabel label : labels) { 50 | canvas.drawText(label.getLabel(), x, y, textPaint); 51 | y = y - 62.0f; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/textrecognition/TextGraphic.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.textrecognition; 15 | 16 | import android.graphics.Canvas; 17 | import android.graphics.Color; 18 | import android.graphics.Paint; 19 | import android.graphics.RectF; 20 | 21 | import com.google.firebase.ml.vision.text.FirebaseVisionText; 22 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 23 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay.Graphic; 24 | 25 | /** 26 | * Graphic instance for rendering TextBlock position, size, and ID within an associated graphic 27 | * overlay view. 28 | */ 29 | public class TextGraphic extends Graphic { 30 | 31 | private static final int TEXT_COLOR = Color.WHITE; 32 | private static final float TEXT_SIZE = 54.0f; 33 | private static final float STROKE_WIDTH = 4.0f; 34 | 35 | private final Paint rectPaint; 36 | private final Paint textPaint; 37 | private final FirebaseVisionText.Element text; 38 | 39 | TextGraphic(GraphicOverlay overlay, FirebaseVisionText.Element text) { 40 | super(overlay); 41 | 42 | this.text = text; 43 | 44 | rectPaint = new Paint(); 45 | rectPaint.setColor(TEXT_COLOR); 46 | rectPaint.setStyle(Paint.Style.STROKE); 47 | rectPaint.setStrokeWidth(STROKE_WIDTH); 48 | 49 | textPaint = new Paint(); 50 | textPaint.setColor(TEXT_COLOR); 51 | textPaint.setTextSize(TEXT_SIZE); 52 | // Redraw the overlay, as this graphic has been added. 53 | postInvalidate(); 54 | } 55 | 56 | /** Draws the text block annotations for position, size, and raw value on the supplied canvas. */ 57 | @Override 58 | public void draw(Canvas canvas) { 59 | if (text == null) { 60 | throw new IllegalStateException("Attempting to draw a null text."); 61 | } 62 | 63 | // Draws the bounding box around the TextBlock. 64 | RectF rect = new RectF(text.getBoundingBox()); 65 | rect.left = translateX(rect.left); 66 | rect.top = translateY(rect.top); 67 | rect.right = translateX(rect.right); 68 | rect.bottom = translateY(rect.bottom); 69 | canvas.drawRect(rect, rectPaint); 70 | 71 | // Renders the text at the bottom of the box. 72 | canvas.drawText(text.getText(), rect.left, rect.bottom, textPaint); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/com/google/firebase/samples/apps/mlkit/textrecognition/TextRecognitionProcessor.java: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | package com.google.firebase.samples.apps.mlkit.textrecognition; 15 | 16 | import android.support.annotation.NonNull; 17 | import android.util.Log; 18 | 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.ml.vision.FirebaseVision; 21 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 22 | import com.google.firebase.ml.vision.text.FirebaseVisionText; 23 | import com.google.firebase.ml.vision.text.FirebaseVisionTextDetector; 24 | import com.google.firebase.samples.apps.mlkit.FrameMetadata; 25 | import com.google.firebase.samples.apps.mlkit.GraphicOverlay; 26 | import com.google.firebase.samples.apps.mlkit.VisionProcessorBase; 27 | 28 | import java.io.IOException; 29 | import java.util.List; 30 | 31 | /** Processor for the text recognition demo. */ 32 | public class TextRecognitionProcessor extends VisionProcessorBase { 33 | 34 | private static final String TAG = "TextRecognitionProcessor"; 35 | 36 | private final FirebaseVisionTextDetector detector; 37 | 38 | public TextRecognitionProcessor() { 39 | detector = FirebaseVision.getInstance().getVisionTextDetector(); 40 | } 41 | 42 | @Override 43 | public void stop() { 44 | try { 45 | detector.close(); 46 | } catch (IOException e) { 47 | Log.e(TAG, "Exception thrown while trying to close Text Detector: " + e); 48 | } 49 | } 50 | 51 | @Override 52 | protected Task detectInImage(FirebaseVisionImage image) { 53 | return detector.detectInImage(image); 54 | } 55 | 56 | @Override 57 | protected void onSuccess( 58 | @NonNull FirebaseVisionText results, 59 | @NonNull FrameMetadata frameMetadata, 60 | @NonNull GraphicOverlay graphicOverlay) { 61 | graphicOverlay.clear(); 62 | List blocks = results.getBlocks(); 63 | for (int i = 0; i < blocks.size(); i++) { 64 | List lines = blocks.get(i).getLines(); 65 | for (int j = 0; j < lines.size(); j++) { 66 | List elements = lines.get(j).getElements(); 67 | for (int k = 0; k < elements.size(); k++) { 68 | GraphicOverlay.Graphic textGraphic = new TextGraphic(graphicOverlay, elements.get(k)); 69 | graphicOverlay.add(textGraphic); 70 | 71 | } 72 | } 73 | } 74 | } 75 | 76 | @Override 77 | protected void onFailure(@NonNull Exception e) { 78 | Log.w(TAG, "Text detection failed." + e); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-hdpi/ic_action_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_switch_camera_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_switch_camera_white_48dp_inset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-hdpi/ic_switch_camera_white_48dp_inset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tile.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-hdpi/tile.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-mdpi/ic_action_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_switch_camera_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_switch_camera_white_48dp_inset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-mdpi/ic_switch_camera_white_48dp_inset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xhdpi/ic_action_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_switch_camera_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_switch_camera_white_48dp_inset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xhdpi/ic_switch_camera_white_48dp_inset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xxhdpi/ic_action_info.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_switch_camera_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_switch_camera_white_48dp_inset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xxhdpi/ic_switch_camera_white_48dp_inset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_switch_camera_white_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_switch_camera_white_48dp_inset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable-xxxhdpi/ic_switch_camera_white_48dp_inset.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/flip_cam_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable/flip_cam_512.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/mlkit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ankitjamuar/android-firebase-mlkit/dddc7109ba623534f254e2505a04a9d8918315f8/app/src/main/res/drawable/mlkit.png -------------------------------------------------------------------------------- /app/src/main/res/layout-land/activity_live_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 15 | 16 | 20 | 21 | 22 | 23 | 30 | 31 | 36 | 37 | 44 | 45 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_chooser.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | 22 | 23 |